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 |
|
32 class TextManager(Manager): |
29 class TextManager(Manager): |
33 def create_text(self, title, format, content, note, name, email, tags, user=None, state='approved', **kwargs): |
30 def create_text(self, title, format, content, note, name, email, tags, user=None, state='approved', **kwargs): |
34 content = on_content_receive(content, format) |
31 content = on_content_receive(content, format) |
35 text = self.create(name=name, email=email, user=user, state=state) |
32 text = self.create(name=name, email=email, user=user, state=state) |
36 text_version = TextVersion.objects.create(title=title, format=format, content=content, text=text, note=note, name=name, email=email, tags=tags, user=user) |
33 text_version = TextVersion.objects.create(title=title, format=format, content=content, text=text, note=note, name=name, email=email, tags=tags, user=user) |
146 text_version.edit(new_title, new_format, new_content, new_tags, new_note, keep_comments, cancel_modified_scopes) |
143 text_version.edit(new_title, new_format, new_content, new_tags, new_note, keep_comments, cancel_modified_scopes) |
147 return text_version |
144 return text_version |
148 |
145 |
149 def __unicode__(self): |
146 def __unicode__(self): |
150 return self.title |
147 return self.title |
|
148 |
|
149 DEFAULT_INPUT_FORMAT = getattr(settings, 'DEFAULT_INPUT_FORMAT', DEFAULT_INPUT_FORMAT_PANDOC) |
|
150 CHOICES_INPUT_FORMATS = getattr(settings, 'CHOICES_INPUT_FORMATS', CHOICES_INPUT_FORMATS_PANDOC) |
|
151 |
|
152 class TextVersionManager(KeyManager): |
|
153 |
|
154 def duplicate(self, text_version, duplicate_comments=True): |
|
155 old_comment_set = set(text_version.comment_set.all()) |
|
156 text_version.id = None |
|
157 |
|
158 # generate new key |
|
159 text_version.key = self._gen_key() |
|
160 text_version.adminkey = self._gen_adminkey() |
|
161 |
|
162 text_version.save() |
|
163 |
|
164 duplicate_text_version = text_version |
|
165 |
|
166 if duplicate_comments: |
|
167 old_comment_map = {} |
|
168 while len(old_comment_set): |
|
169 for c in old_comment_set: |
|
170 if not c.reply_to or c.reply_to.id in old_comment_map: |
|
171 old_id = c.id |
|
172 old_comment_set.remove(c) |
|
173 reply_to = None |
|
174 if c.reply_to: |
|
175 reply_to = old_comment_map[c.reply_to.id] |
|
176 c2 = Comment.objects.duplicate(c, duplicate_text_version, reply_to, keep_dates=True) |
|
177 old_comment_map[old_id] = c2 |
|
178 break |
|
179 |
|
180 return duplicate_text_version |
|
181 |
|
182 class TextVersion(AuthorModel, KeyModel): |
|
183 modified = models.DateTimeField(auto_now=True) |
|
184 created = models.DateTimeField(auto_now_add=True) |
|
185 |
|
186 title = models.TextField(ugettext_lazy("Title")) |
|
187 format = models.CharField(ugettext_lazy("Format"), max_length=20, blank=False, default=DEFAULT_INPUT_FORMAT, choices=CHOICES_INPUT_FORMATS) |
|
188 content = models.TextField(ugettext_lazy("Content")) |
|
189 tags = TagField(ugettext_lazy("Tags"), max_length=1000) |
|
190 |
|
191 note = models.CharField(ugettext_lazy("Note"), max_length=100, null=True, blank=True) |
|
192 |
|
193 mod_posteriori = models.BooleanField(ugettext_lazy('Moderation a posteriori?'), default=True) |
|
194 |
|
195 from django.utils.safestring import mark_safe |
|
196 |
|
197 category_1 = models.CharField(ugettext_lazy("Label for the first category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #1523f4"> </span><br />' + _("Leave blank to use the value configured for the workspace.") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
198 category_2 = models.CharField(ugettext_lazy("Label for the second category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #f4154f"> </span><br />' + _("Leave blank to use the value configured for the workspace. ") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
199 category_3 = models.CharField(ugettext_lazy("Label for the third category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #09ff09"> </span><br />' + _("Leave blank to use the value configured for the workspace. ") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
200 category_4 = models.CharField(ugettext_lazy("Label for the fourth category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #bc39cf"> </span><br />' + _("Leave blank to use the value configured for the workspace. ") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
201 category_5 = models.CharField(ugettext_lazy("Label for the fifth category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #ffbd08"> </span><br />' + _("Leave blank to use the value configured for the workspace. ") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
202 |
|
203 text = models.ForeignKey("Text") |
|
204 |
|
205 objects = TextVersionManager() |
|
206 |
|
207 def get_content(self, format='html'): |
|
208 return pandoc_convert(self.content, self.format, format) |
|
209 |
|
210 def get_comments(self): |
|
211 "Warning: data access without security" |
|
212 return self.comment_set.filter(reply_to=None, deleted=False) |
|
213 |
|
214 def get_replies(self): |
|
215 "Warning: data access without security" |
|
216 return self.comment_set.filter(~Q(reply_to == None), Q(deleted=False)) |
|
217 |
|
218 def __unicode__(self): |
|
219 return '<%d> %s' % (self.id, self.title) |
|
220 |
|
221 def edit(self, new_title, new_format, new_content, new_tags=None, new_note=None, keep_comments=True, cancel_modified_scopes=True): |
|
222 new_content = on_content_receive(new_content, new_format) |
|
223 if not keep_comments : |
|
224 self.comment_set.all().delete() |
|
225 elif self.content != new_content or new_format != self.format: |
|
226 comments = self.get_comments() ; |
|
227 tomodify_comments, toremove_comments = compute_new_comment_positions(self.content, self.format, new_content, new_format, comments) |
|
228 [comment.save(keep_dates=True) for comment in tomodify_comments] |
|
229 if cancel_modified_scopes : |
|
230 [comment.remove_scope() for comment in toremove_comments] |
|
231 else : |
|
232 [comment.delete() for comment in toremove_comments] |
|
233 |
|
234 self.title = new_title |
|
235 if new_tags: |
|
236 self.tags = new_tags |
|
237 if new_note: |
|
238 self.note = new_note |
|
239 self.content = new_content |
|
240 self.format = new_format |
|
241 self.save() |
|
242 |
|
243 def get_next_version(self): |
|
244 other_versions = TextVersion.objects.filter(text__exact=self.text).order_by('created').filter(created__gt=self.created) |
|
245 return other_versions[0] if other_versions else None |
|
246 if other_versions: |
|
247 return |
|
248 else: |
|
249 return None |
|
250 |
|
251 def get_previous_version(self): |
|
252 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 |
|
254 |
|
255 def get_version_number(self): |
|
256 return TextVersion.objects.filter(text__exact=self.text).order_by('created').filter(created__lte=self.created).count() |
151 |
257 |
152 class CommentManager(Manager): |
258 class CommentManager(Manager): |
153 |
259 |
154 def duplicate(self, comment, text_version, reply_to=None, keep_dates=False): |
260 def duplicate(self, comment, text_version, reply_to=None, keep_dates=False): |
155 comment.id = None |
261 comment.id = None |
309 objects = ConfigurationManager() |
415 objects = ConfigurationManager() |
310 |
416 |
311 def __unicode__(self): |
417 def __unicode__(self): |
312 return '%s: %s' % (self.key, self.value) |
418 return '%s: %s' % (self.key, self.value) |
313 |
419 |
314 class TextVersionManager(KeyManager): |
|
315 |
|
316 def duplicate(self, text_version, duplicate_comments=True): |
|
317 old_comment_set = set(text_version.comment_set.all()) |
|
318 text_version.id = None |
|
319 |
|
320 # generate new key |
|
321 text_version.key = self._gen_key() |
|
322 text_version.adminkey = self._gen_adminkey() |
|
323 |
|
324 text_version.save() |
|
325 |
|
326 duplicate_text_version = text_version |
|
327 |
|
328 if duplicate_comments: |
|
329 old_comment_map = {} |
|
330 while len(old_comment_set): |
|
331 for c in old_comment_set: |
|
332 if not c.reply_to or c.reply_to.id in old_comment_map: |
|
333 old_id = c.id |
|
334 old_comment_set.remove(c) |
|
335 reply_to = None |
|
336 if c.reply_to: |
|
337 reply_to = old_comment_map[c.reply_to.id] |
|
338 c2 = Comment.objects.duplicate(c, duplicate_text_version, reply_to, keep_dates=True) |
|
339 old_comment_map[old_id] = c2 |
|
340 break |
|
341 |
|
342 return duplicate_text_version |
|
343 |
|
344 class TextVersion(AuthorModel, KeyModel): |
|
345 modified = models.DateTimeField(auto_now=True) |
|
346 created = models.DateTimeField(auto_now_add=True) |
|
347 |
|
348 title = models.TextField(ugettext_lazy("Title")) |
|
349 format = models.CharField(ugettext_lazy("Format"), max_length=20, blank=False, default=DEFAULT_INPUT_FORMAT, choices=CHOICES_INPUT_FORMATS) |
|
350 content = models.TextField(ugettext_lazy("Content")) |
|
351 tags = TagField(ugettext_lazy("Tags"), max_length=1000) |
|
352 |
|
353 note = models.CharField(ugettext_lazy("Note"), max_length=100, null=True, blank=True) |
|
354 |
|
355 mod_posteriori = models.BooleanField(ugettext_lazy('Moderation a posteriori?'), default=True) |
|
356 |
|
357 from django.utils.safestring import mark_safe |
|
358 |
|
359 category_1 = models.CharField(ugettext_lazy("Label for the first category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #1523f4"> </span><br />' + _("Leave blank to use the value configured for the workspace.") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
360 category_2 = models.CharField(ugettext_lazy("Label for the second category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #f4154f"> </span><br />' + _("Leave blank to use the value configured for the workspace. ") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
361 category_3 = models.CharField(ugettext_lazy("Label for the third category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #09ff09"> </span><br />' + _("Leave blank to use the value configured for the workspace. ") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
362 category_4 = models.CharField(ugettext_lazy("Label for the fourth category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #bc39cf"> </span><br />' + _("Leave blank to use the value configured for the workspace. ") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
363 category_5 = models.CharField(ugettext_lazy("Label for the fifth category of comments"), help_text=mark_safe(_("Paragraphs including at least one comment of this category will have a vertical bar with this color: ") + '<span style="width: 2px; height: 5px; background-color: #ffbd08"> </span><br />' + _("Leave blank to use the value configured for the workspace. ") + '<br />' + _("To disable this category for this text whatever the configuration for the workspace, enter: ") + '<em>none</em>'), max_length=20, null=True, blank=True) |
|
364 |
|
365 text = models.ForeignKey("Text") |
|
366 |
|
367 objects = TextVersionManager() |
|
368 |
|
369 def get_content(self, format='html'): |
|
370 return pandoc_convert(self.content, self.format, format) |
|
371 |
|
372 def get_comments(self): |
|
373 "Warning: data access without security" |
|
374 return self.comment_set.filter(reply_to=None, deleted=False) |
|
375 |
|
376 def get_replies(self): |
|
377 "Warning: data access without security" |
|
378 return self.comment_set.filter(~Q(reply_to == None), Q(deleted=False)) |
|
379 |
|
380 def __unicode__(self): |
|
381 return '<%d> %s' % (self.id, self.title) |
|
382 |
|
383 def edit(self, new_title, new_format, new_content, new_tags=None, new_note=None, keep_comments=True, cancel_modified_scopes=True): |
|
384 new_content = on_content_receive(new_content, new_format) |
|
385 if not keep_comments : |
|
386 self.comment_set.all().delete() |
|
387 elif self.content != new_content or new_format != self.format: |
|
388 comments = self.get_comments() ; |
|
389 tomodify_comments, toremove_comments = compute_new_comment_positions(self.content, self.format, new_content, new_format, comments) |
|
390 [comment.save(keep_dates=True) for comment in tomodify_comments] |
|
391 if cancel_modified_scopes : |
|
392 [comment.remove_scope() for comment in toremove_comments] |
|
393 else : |
|
394 [comment.delete() for comment in toremove_comments] |
|
395 |
|
396 self.title = new_title |
|
397 if new_tags: |
|
398 self.tags = new_tags |
|
399 if new_note: |
|
400 self.note = new_note |
|
401 self.content = new_content |
|
402 self.format = new_format |
|
403 self.save() |
|
404 |
|
405 def get_next_version(self): |
|
406 other_versions = TextVersion.objects.filter(text__exact=self.text).order_by('created').filter(created__gt=self.created) |
|
407 return other_versions[0] if other_versions else None |
|
408 if other_versions: |
|
409 return |
|
410 else: |
|
411 return None |
|
412 |
|
413 def get_previous_version(self): |
|
414 other_versions = TextVersion.objects.filter(text__exact=self.text).order_by('-created').filter(created__lt=self.created) |
|
415 return other_versions[0] if other_versions else None |
|
416 |
|
417 def get_version_number(self): |
|
418 return TextVersion.objects.filter(text__exact=self.text).order_by('created').filter(created__lte=self.created).count() |
|
419 |
|
420 class AttachmentManager(KeyManager): |
420 class AttachmentManager(KeyManager): |
421 def create_attachment(self, text_version, filename, data): |
421 def create_attachment(self, text_version, filename, data): |
422 attach = self.create(text_version=text_version) |
422 attach = self.create(text_version=text_version) |
423 ff = ContentFile(data) |
423 ff = ContentFile(data) |
424 attach.data.save(filename, ff) |
424 attach.data.save(filename, ff) |