24 from tagging.fields import TagField |
24 from tagging.fields import TagField |
25 import pickle |
25 import pickle |
26 from django.db import connection |
26 from django.db import connection |
27 from datetime import datetime |
27 from datetime import datetime |
28 |
28 |
|
29 DEFAULT_INPUT_FORMAT = getattr(settings, 'DEFAULT_INPUT_FORMAT', DEFAULT_INPUT_FORMAT_PANDOC) |
|
30 CHOICES_INPUT_FORMATS = getattr(settings, 'CHOICES_INPUT_FORMATS', CHOICES_INPUT_FORMATS_PANDOC) |
|
31 |
29 class TextManager(Manager): |
32 class TextManager(Manager): |
30 def create_text(self, title, format, content, note, name, email, tags, user=None, state='approved', **kwargs): |
33 def create_text(self, title, format, content, note, name, email, tags, user=None, state='approved', **kwargs): |
31 content = on_content_receive(content, format) |
34 content = on_content_receive(content, format) |
32 text = self.create(name=name, email=email, user=user, state=state) |
35 text = self.create(name=name, email=email, user=user, state=state) |
33 text_version = TextVersion.objects.create(title=title, format=format, content=content, text=text, note=note, name=name, email=email, tags=tags, user=user) |
36 text_version = TextVersion.objects.create(title=title, format=format, content=content, text=text, note=note, name=name, email=email, tags=tags, user=user) |
144 return text_version |
147 return text_version |
145 |
148 |
146 def __unicode__(self): |
149 def __unicode__(self): |
147 return self.title |
150 return self.title |
148 |
151 |
149 DEFAULT_INPUT_FORMAT = getattr(settings, 'DEFAULT_INPUT_FORMAT', DEFAULT_INPUT_FORMAT_PANDOC) |
152 class CommentManager(Manager): |
150 CHOICES_INPUT_FORMATS = getattr(settings, 'CHOICES_INPUT_FORMATS', CHOICES_INPUT_FORMATS_PANDOC) |
153 |
|
154 def duplicate(self, comment, text_version, reply_to=None, keep_dates=False): |
|
155 comment.id = None |
|
156 comment.text_version = text_version |
|
157 if reply_to: |
|
158 comment.reply_to = reply_to |
|
159 self.update_keys(comment) |
|
160 comment.save(keep_dates=keep_dates) |
|
161 return comment |
|
162 |
|
163 from cm.models_base import KEY_MAX_SIZE, generate_key |
|
164 |
|
165 class Comment(PermanentModel, AuthorModel): |
|
166 modified = models.DateTimeField() |
|
167 created = models.DateTimeField() |
|
168 |
|
169 # key to identify same comments across versions |
|
170 id_key = models.CharField(max_length=KEY_MAX_SIZE, db_index=True, default=generate_key) |
|
171 |
|
172 text_version = models.ForeignKey("TextVersion") |
|
173 |
|
174 # comment_set will be replies |
|
175 reply_to = models.ForeignKey("Comment", null=True, blank=True) |
|
176 |
|
177 title = models.TextField() |
|
178 content = models.TextField() |
|
179 content_html = models.TextField() |
|
180 |
|
181 format = models.CharField(_("Format:"), max_length=20, blank=False, default=DEFAULT_INPUT_FORMAT, choices=CHOICES_INPUT_FORMATS) |
|
182 |
|
183 tags = TagField() |
|
184 |
|
185 category = models.PositiveSmallIntegerField(default=0) |
|
186 |
|
187 start_wrapper = models.IntegerField(null=True, blank=True) |
|
188 end_wrapper = models.IntegerField(null=True, blank=True) |
|
189 start_offset = models.IntegerField(null=True, blank=True) |
|
190 end_offset = models.IntegerField(null=True, blank=True) |
|
191 |
|
192 objects = CommentManager() |
|
193 |
|
194 def save(self, keep_dates=False, **kwargs): |
|
195 if not keep_dates: |
|
196 now = datetime.now() |
|
197 if not self.id: |
|
198 self.created = now |
|
199 self.modified = now |
|
200 super(PermanentModel, self).save(**kwargs) |
|
201 |
|
202 def __unicode__(self): |
|
203 return '<%d> %s [st_wrp:%s, st_ofs:%s, e_wrp:%s, e_ofs:%s]' % (self.id, self.title, self.start_wrapper , self.start_offset, self.end_wrapper, self.end_offset, ) |
|
204 |
|
205 def is_reply(self): |
|
206 return self.reply_to != None |
|
207 |
|
208 def is_thread_full_visible(self, own_user=None): |
|
209 """ |
|
210 own_user: comment belonging to this user are also visible |
|
211 """ |
|
212 if self.state == 'approved' or (own_user and self.user == own_user): |
|
213 if self.reply_to==None: |
|
214 return True |
|
215 else: |
|
216 return self.reply_to.is_thread_full_visible(own_user) |
|
217 return False |
|
218 |
|
219 def is_scope_removed(self): |
|
220 #when scope is "removed" we will have |
|
221 #self.start_wrapper == self.end_wrapper == self.start_offset == self.end_offset == -1 |
|
222 return (self.start_wrapper == -1) |
|
223 |
|
224 def top_comment(self): |
|
225 if self.reply_to == None : |
|
226 return self |
|
227 else : |
|
228 return self.reply_to.top_comment() |
|
229 |
|
230 def depth(self): |
|
231 if self.reply_to == None : |
|
232 return 0 |
|
233 else : |
|
234 return 1 + self.reply_to.depth() |
|
235 |
|
236 def delete(self): |
|
237 PermanentModel.delete(self) |
|
238 # delete replies |
|
239 [c.delete() for c in self.comment_set.all()] |
|
240 |
|
241 def remove_scope(self): |
|
242 self.start_wrapper = self.end_wrapper = self.start_offset = self.end_offset = -1 |
|
243 self.save() |
|
244 |
|
245 # http://docs.djangoproject.com/en/dev/topics/files/#topics-files |
|
246 |
|
247 # default conf values |
|
248 DEFAULT_CONF = { |
|
249 'workspace_name' : 'Workspace', |
|
250 'site_url' : settings.SITE_URL, |
|
251 'email_from' : settings.DEFAULT_FROM_EMAIL, |
|
252 } |
|
253 |
|
254 from cm.role_models import change_role_model |
|
255 |
|
256 class ConfigurationManager(models.Manager): |
|
257 def set_workspace_name(self, workspace_name): |
|
258 if workspace_name: |
|
259 self.set_key('workspace_name', workspace_name) |
|
260 |
|
261 def get_key(self, key, default_value=None): |
|
262 try: |
|
263 return self.get(key=key).value |
|
264 except Configuration.DoesNotExist: |
|
265 return DEFAULT_CONF.get(key, default_value) |
|
266 |
|
267 def del_key(self, key): |
|
268 try: |
|
269 self.get(key=key).delete() |
|
270 except Configuration.DoesNotExist: |
|
271 return None |
|
272 |
|
273 def set_key(self, key, value): |
|
274 conf, created = self.get_or_create(key=key) |
|
275 if created or conf.value != value: |
|
276 conf.value = value |
|
277 conf.save() |
|
278 if key == 'workspace_role_model': |
|
279 change_role_model(value) |
|
280 |
|
281 def __getitem__(self, key): |
|
282 if not key.startswith('f_'): |
|
283 return self.get_key(key, None) |
|
284 else: |
|
285 return getattr(self,key)() |
|
286 |
|
287 def f_get_logo_url(self): |
|
288 key = self.get_key('workspace_logo_file_key', None) |
|
289 if key: |
|
290 attach = Attachment.objects.get(key=key) |
|
291 return attach.data.url |
|
292 else: |
|
293 return None |
|
294 |
|
295 import base64 |
|
296 |
|
297 class Configuration(models.Model): |
|
298 key = models.TextField(blank=False) # , unique=True cannot be added: creates error on mysql (?) |
|
299 raw_value = models.TextField(blank=False) |
|
300 |
|
301 def get_value(self): |
|
302 return pickle.loads(base64.b64decode(self.raw_value.encode('utf8'))) |
|
303 |
|
304 def set_value(self, value): |
|
305 self.raw_value = base64.b64encode(pickle.dumps(value, 0)).encode('utf8') |
|
306 |
|
307 value = property(get_value, set_value) |
|
308 |
|
309 objects = ConfigurationManager() |
|
310 |
|
311 def __unicode__(self): |
|
312 return '%s: %s' % (self.key, self.value) |
|
313 |
|
314 ApplicationConfiguration = Configuration.objects |
151 |
315 |
152 class TextVersionManager(KeyManager): |
316 class TextVersionManager(KeyManager): |
153 |
317 |
154 def duplicate(self, text_version, duplicate_comments=True): |
318 def duplicate(self, text_version, duplicate_comments=True): |
155 old_comment_set = set(text_version.comment_set.all()) |
319 old_comment_set = set(text_version.comment_set.all()) |
252 other_versions = TextVersion.objects.filter(text__exact=self.text).order_by('-created').filter(created__lt=self.created) |
416 other_versions = TextVersion.objects.filter(text__exact=self.text).order_by('-created').filter(created__lt=self.created) |
253 return other_versions[0] if other_versions else None |
417 return other_versions[0] if other_versions else None |
254 |
418 |
255 def get_version_number(self): |
419 def get_version_number(self): |
256 return TextVersion.objects.filter(text__exact=self.text).order_by('created').filter(created__lte=self.created).count() |
420 return TextVersion.objects.filter(text__exact=self.text).order_by('created').filter(created__lte=self.created).count() |
257 |
|
258 class CommentManager(Manager): |
|
259 |
|
260 def duplicate(self, comment, text_version, reply_to=None, keep_dates=False): |
|
261 comment.id = None |
|
262 comment.text_version = text_version |
|
263 if reply_to: |
|
264 comment.reply_to = reply_to |
|
265 self.update_keys(comment) |
|
266 comment.save(keep_dates=keep_dates) |
|
267 return comment |
|
268 |
|
269 from cm.models_base import KEY_MAX_SIZE, generate_key |
|
270 |
|
271 class Comment(PermanentModel, AuthorModel): |
|
272 modified = models.DateTimeField() |
|
273 created = models.DateTimeField() |
|
274 |
|
275 # key to identify same comments across versions |
|
276 id_key = models.CharField(max_length=KEY_MAX_SIZE, db_index=True, default=generate_key) |
|
277 |
|
278 text_version = models.ForeignKey("TextVersion") |
|
279 |
|
280 # comment_set will be replies |
|
281 reply_to = models.ForeignKey("Comment", null=True, blank=True) |
|
282 |
|
283 title = models.TextField() |
|
284 content = models.TextField() |
|
285 content_html = models.TextField() |
|
286 |
|
287 format = models.CharField(_("Format:"), max_length=20, blank=False, default=DEFAULT_INPUT_FORMAT, choices=CHOICES_INPUT_FORMATS) |
|
288 |
|
289 tags = TagField() |
|
290 |
|
291 category = models.PositiveSmallIntegerField(default=0) |
|
292 |
|
293 start_wrapper = models.IntegerField(null=True, blank=True) |
|
294 end_wrapper = models.IntegerField(null=True, blank=True) |
|
295 start_offset = models.IntegerField(null=True, blank=True) |
|
296 end_offset = models.IntegerField(null=True, blank=True) |
|
297 |
|
298 objects = CommentManager() |
|
299 |
|
300 def save(self, keep_dates=False, **kwargs): |
|
301 if not keep_dates: |
|
302 now = datetime.now() |
|
303 if not self.id: |
|
304 self.created = now |
|
305 self.modified = now |
|
306 super(PermanentModel, self).save(**kwargs) |
|
307 |
|
308 def __unicode__(self): |
|
309 return '<%d> %s [st_wrp:%s, st_ofs:%s, e_wrp:%s, e_ofs:%s]' % (self.id, self.title, self.start_wrapper , self.start_offset, self.end_wrapper, self.end_offset, ) |
|
310 |
|
311 def is_reply(self): |
|
312 return self.reply_to != None |
|
313 |
|
314 def is_thread_full_visible(self, own_user=None): |
|
315 """ |
|
316 own_user: comment belonging to this user are also visible |
|
317 """ |
|
318 if self.state == 'approved' or (own_user and self.user == own_user): |
|
319 if self.reply_to==None: |
|
320 return True |
|
321 else: |
|
322 return self.reply_to.is_thread_full_visible(own_user) |
|
323 return False |
|
324 |
|
325 def is_scope_removed(self): |
|
326 #when scope is "removed" we will have |
|
327 #self.start_wrapper == self.end_wrapper == self.start_offset == self.end_offset == -1 |
|
328 return (self.start_wrapper == -1) |
|
329 |
|
330 def top_comment(self): |
|
331 if self.reply_to == None : |
|
332 return self |
|
333 else : |
|
334 return self.reply_to.top_comment() |
|
335 |
|
336 def depth(self): |
|
337 if self.reply_to == None : |
|
338 return 0 |
|
339 else : |
|
340 return 1 + self.reply_to.depth() |
|
341 |
|
342 def delete(self): |
|
343 PermanentModel.delete(self) |
|
344 # delete replies |
|
345 [c.delete() for c in self.comment_set.all()] |
|
346 |
|
347 def remove_scope(self): |
|
348 self.start_wrapper = self.end_wrapper = self.start_offset = self.end_offset = -1 |
|
349 self.save() |
|
350 |
|
351 # http://docs.djangoproject.com/en/dev/topics/files/#topics-files |
|
352 |
|
353 # default conf values |
|
354 DEFAULT_CONF = { |
|
355 'workspace_name' : 'Workspace', |
|
356 'site_url' : settings.SITE_URL, |
|
357 'email_from' : settings.DEFAULT_FROM_EMAIL, |
|
358 } |
|
359 |
|
360 from cm.role_models import change_role_model |
|
361 |
|
362 class ConfigurationManager(models.Manager): |
|
363 def set_workspace_name(self, workspace_name): |
|
364 if workspace_name: |
|
365 self.set_key('workspace_name', workspace_name) |
|
366 |
|
367 def get_key(self, key, default_value=None): |
|
368 try: |
|
369 return self.get(key=key).value |
|
370 except Configuration.DoesNotExist: |
|
371 return DEFAULT_CONF.get(key, default_value) |
|
372 |
|
373 def del_key(self, key): |
|
374 try: |
|
375 self.get(key=key).delete() |
|
376 except Configuration.DoesNotExist: |
|
377 return None |
|
378 |
|
379 def set_key(self, key, value): |
|
380 conf, created = self.get_or_create(key=key) |
|
381 if created or conf.value != value: |
|
382 conf.value = value |
|
383 conf.save() |
|
384 if key == 'workspace_role_model': |
|
385 change_role_model(value) |
|
386 |
|
387 def __getitem__(self, key): |
|
388 if not key.startswith('f_'): |
|
389 return self.get_key(key, None) |
|
390 else: |
|
391 return getattr(self,key)() |
|
392 |
|
393 def f_get_logo_url(self): |
|
394 key = self.get_key('workspace_logo_file_key', None) |
|
395 if key: |
|
396 attach = Attachment.objects.get(key=key) |
|
397 return attach.data.url |
|
398 else: |
|
399 return None |
|
400 |
|
401 import base64 |
|
402 |
|
403 class Configuration(models.Model): |
|
404 key = models.TextField(blank=False) # , unique=True cannot be added: creates error on mysql (?) |
|
405 raw_value = models.TextField(blank=False) |
|
406 |
|
407 def get_value(self): |
|
408 return pickle.loads(base64.b64decode(self.raw_value.encode('utf8'))) |
|
409 |
|
410 def set_value(self, value): |
|
411 self.raw_value = base64.b64encode(pickle.dumps(value, 0)).encode('utf8') |
|
412 |
|
413 value = property(get_value, set_value) |
|
414 |
|
415 objects = ConfigurationManager() |
|
416 |
|
417 def __unicode__(self): |
|
418 return '%s: %s' % (self.key, self.value) |
|
419 |
|
420 ApplicationConfiguration = Configuration.objects |
|
421 |
421 |
422 class AttachmentManager(KeyManager): |
422 class AttachmentManager(KeyManager): |
423 def create_attachment(self, text_version, filename, data): |
423 def create_attachment(self, text_version, filename, data): |
424 attach = self.create(text_version=text_version) |
424 attach = self.create(text_version=text_version) |
425 ff = ContentFile(data) |
425 ff = ContentFile(data) |