| author | gibus |
| Tue, 25 Jun 2013 11:56:01 +0200 | |
| changeset 504 | b2e0186daa5b |
| parent 478 | b9772b94b624 |
| child 511 | 54d72b4da009 |
| permissions | -rw-r--r-- |
|
156
6d447220fd1e
bug fix: removed \r in textversion's content because of a comment position bug in pre_edit. Added a save function to TextVersion to do so.
rbernard
parents:
155
diff
changeset
|
1 |
import re |
| 0 | 2 |
from cm.converters.pandoc_converters import \ |
3 |
CHOICES_INPUT_FORMATS as CHOICES_INPUT_FORMATS_PANDOC, \ |
|
4 |
DEFAULT_INPUT_FORMAT as DEFAULT_INPUT_FORMAT_PANDOC, pandoc_convert |
|
5 |
from cm.models_base import PermanentModel, KeyManager, Manager, KeyModel, AuthorModel |
|
6 |
from cm.models_utils import * |
|
7 |
from cm.utils.dj import absolute_reverse |
|
8 |
from cm.utils.date import datetime_to_user_str |
|
| 175 | 9 |
from cm.utils.html import on_content_receive |
| 0 | 10 |
from cm.utils.comment_positioning import compute_new_comment_positions |
11 |
from django import forms |
|
12 |
from django.db.models import Q |
|
13 |
from django.template.loader import render_to_string |
|
14 |
from django.conf import settings |
|
15 |
from django.template import RequestContext |
|
16 |
from django.contrib.auth.models import Permission |
|
17 |
from django.contrib.contenttypes import generic |
|
18 |
from django.contrib.contenttypes.models import ContentType |
|
19 |
from django.core.files.base import ContentFile |
|
20 |
from django.core.urlresolvers import reverse |
|
21 |
from django.template.defaultfilters import timesince |
|
22 |
from django.db import models |
|
23 |
from django.utils.translation import ugettext as _, ugettext_lazy, ugettext_noop |
|
24 |
from tagging.fields import TagField |
|
25 |
import pickle |
|
26 |
from django.db import connection |
|
|
72
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
27 |
from datetime import datetime |
| 0 | 28 |
|
|
504
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
29 |
# default conf values |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
30 |
DEFAULT_CONF = { |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
31 |
'workspace_name' : 'Workspace', |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
32 |
'site_url' : settings.SITE_URL, |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
33 |
'email_from' : settings.DEFAULT_FROM_EMAIL, |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
34 |
} |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
35 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
36 |
from cm.role_models import change_role_model |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
37 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
38 |
class ConfigurationManager(models.Manager): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
39 |
def set_workspace_name(self, workspace_name): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
40 |
if workspace_name: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
41 |
self.set_key('workspace_name', workspace_name) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
42 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
43 |
def get_key(self, key, default_value=None): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
44 |
try: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
45 |
return self.get(key=key).value |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
46 |
except Configuration.DoesNotExist: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
47 |
return DEFAULT_CONF.get(key, default_value) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
48 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
49 |
def del_key(self, key): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
50 |
try: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
51 |
self.get(key=key).delete() |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
52 |
except Configuration.DoesNotExist: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
53 |
return None |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
54 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
55 |
def set_key(self, key, value): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
56 |
conf, created = self.get_or_create(key=key) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
57 |
if created or conf.value != value: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
58 |
conf.value = value |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
59 |
conf.save() |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
60 |
if key == 'workspace_role_model': |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
61 |
change_role_model(value) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
62 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
63 |
def __getitem__(self, key): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
64 |
if not key.startswith('f_'): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
65 |
return self.get_key(key, None) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
66 |
else: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
67 |
return getattr(self,key)() |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
68 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
69 |
def f_get_logo_url(self): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
70 |
key = self.get_key('workspace_logo_file_key', None) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
71 |
if key: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
72 |
attach = Attachment.objects.get(key=key) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
73 |
return attach.data.url |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
74 |
else: |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
75 |
return None |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
76 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
77 |
import base64 |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
78 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
79 |
class Configuration(models.Model): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
80 |
key = models.TextField(blank=False) # , unique=True cannot be added: creates error on mysql (?) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
81 |
raw_value = models.TextField(blank=False) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
82 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
83 |
def get_value(self): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
84 |
return pickle.loads(base64.b64decode(self.raw_value.encode('utf8'))) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
85 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
86 |
def set_value(self, value): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
87 |
self.raw_value = base64.b64encode(pickle.dumps(value, 0)).encode('utf8') |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
88 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
89 |
value = property(get_value, set_value) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
90 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
91 |
objects = ConfigurationManager() |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
92 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
93 |
def __unicode__(self): |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
94 |
return '%s: %s' % (self.key, self.value) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
95 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
96 |
ApplicationConfiguration = Configuration.objects |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
97 |
|
| 0 | 98 |
class TextManager(Manager): |
99 |
def create_text(self, title, format, content, note, name, email, tags, user=None, state='approved', **kwargs): |
|
| 175 | 100 |
content = on_content_receive(content, format) |
| 0 | 101 |
text = self.create(name=name, email=email, user=user, state=state) |
102 |
text_version = TextVersion.objects.create(title=title, format=format, content=content, text=text, note=note, name=name, email=email, tags=tags, user=user) |
|
103 |
return text |
|
104 |
||
105 |
def create_new_version(self, text, title, format, content, note, name, email, tags, user=None, **kwargs): |
|
106 |
text_version = TextVersion.objects.create(title=title, format=format, content=content, text=text, note=note, name=name, email=email, tags=tags, user=user) |
|
107 |
return text_version |
|
108 |
||
109 |
class Text(PermanentModel, AuthorModel): |
|
110 |
modified = models.DateTimeField(auto_now=True) |
|
111 |
created = models.DateTimeField(auto_now_add=True) |
|
112 |
||
113 |
private_feed_key = models.CharField(max_length=20, db_index=True, unique=True, blank=True, null=True, default=None) |
|
114 |
||
115 |
# denormalized fields |
|
|
407
2d14a80716e2
When last_version is deleted, do not delete text and previous versions in cascade, but really delete text only when there is no previous version, otherwise update text.last_version with previous version.
gibus
parents:
347
diff
changeset
|
116 |
last_text_version = models.ForeignKey("TextVersion", related_name='related_text', null=True, blank=True, on_delete=models.SET_NULL) |
| 0 | 117 |
title = models.TextField() |
118 |
||
119 |
objects = TextManager() |
|
120 |
||
121 |
def get_latest_version(self): |
|
122 |
return self.last_text_version |
|
123 |
||
124 |
def fetch_latest_version(self): |
|
125 |
versions = self.get_versions() |
|
126 |
if versions: |
|
127 |
return versions[0] |
|
128 |
else: |
|
129 |
return None |
|
130 |
||
131 |
def update_denorm_fields(self): |
|
132 |
real_last_text_version = self.fetch_latest_version() |
|
133 |
||
| 289 | 134 |
try: |
135 |
last_text_version = self.last_text_version |
|
136 |
except TextVersion.DoesNotExist: |
|
137 |
# the text version has just been deleted |
|
138 |
last_text_version = None |
|
139 |
||
| 0 | 140 |
modif = False |
| 289 | 141 |
if real_last_text_version and real_last_text_version != last_text_version: |
| 0 | 142 |
self.last_text_version = real_last_text_version |
143 |
modif = True |
|
144 |
||
145 |
if real_last_text_version and real_last_text_version.title and real_last_text_version.title != self.title: |
|
146 |
self.title = real_last_text_version.title |
|
147 |
modif = True |
|
148 |
||
149 |
if real_last_text_version and real_last_text_version.modified != self.modified: |
|
150 |
self.modified = real_last_text_version.modified |
|
151 |
modif = True |
|
152 |
||
153 |
if modif: |
|
154 |
self.save() |
|
155 |
||
|
407
2d14a80716e2
When last_version is deleted, do not delete text and previous versions in cascade, but really delete text only when there is no previous version, otherwise update text.last_version with previous version.
gibus
parents:
347
diff
changeset
|
156 |
# GIB: there is no more version for this text => delete it |
|
2d14a80716e2
When last_version is deleted, do not delete text and previous versions in cascade, but really delete text only when there is no previous version, otherwise update text.last_version with previous version.
gibus
parents:
347
diff
changeset
|
157 |
if real_last_text_version == None and last_text_version == None: |
|
2d14a80716e2
When last_version is deleted, do not delete text and previous versions in cascade, but really delete text only when there is no previous version, otherwise update text.last_version with previous version.
gibus
parents:
347
diff
changeset
|
158 |
self.real_delete() |
| 0 | 159 |
|
160 |
def get_title(self): |
|
161 |
return self.get_latest_version().title |
|
162 |
||
163 |
def get_versions(self): |
|
164 |
""" |
|
165 |
Versions with most recent first |
|
166 |
""" |
|
167 |
versions = TextVersion.objects.filter(text__exact=self).order_by('-created') |
|
168 |
# TODO: use new postgresql 8.4 row_number as extra select to do that |
|
| 145 | 169 |
#for index in xrange(len(versions)): |
170 |
# v = versions[index] |
|
171 |
# # version_number is 1-based |
|
172 |
# setattr(v, 'version_number', len(versions) - index) |
|
| 0 | 173 |
return versions |
174 |
||
175 |
def get_version(self, version_number): |
|
176 |
""" |
|
177 |
Get version number 'version_number' (1-based) |
|
178 |
""" |
|
179 |
version = TextVersion.objects.filter(text__exact=self).order_by('created')[version_number - 1:version_number][0] |
|
180 |
return version |
|
181 |
||
182 |
def get_inversed_versions(self): |
|
183 |
versions = TextVersion.objects.filter(text__exact=self).order_by('created') |
|
184 |
# TODO: use new postgresql 8.4 row_number as extra select to do that |
|
| 145 | 185 |
#for index in xrange(len(versions)): |
186 |
# v = versions[index] |
|
187 |
# # version_number is 1-based |
|
188 |
# setattr(v, 'version_number', index + 1) |
|
| 0 | 189 |
return versions |
190 |
||
191 |
def get_versions_number(self): |
|
192 |
return self.get_versions().count() |
|
193 |
||
194 |
def is_admin(self, adminkey=None): |
|
195 |
if adminkey and self.adminkey == adminkey: |
|
196 |
return True |
|
197 |
else: |
|
198 |
return False |
|
199 |
||
| 145 | 200 |
def revert_to_version(self, text_version_key): |
201 |
text_version = TextVersion.objects.get(key=text_version_key) |
|
202 |
if text_version.text != self: |
|
203 |
raise Exception('Invalid revert attempt') |
|
| 0 | 204 |
new_text_version = TextVersion.objects.duplicate(text_version, True) |
205 |
return new_text_version |
|
206 |
||
| 103 | 207 |
def edit(self, new_title, new_format, new_content, new_tags=None, new_note=None, keep_comments=True, cancel_modified_scopes=True, new_version=True): |
| 0 | 208 |
text_version = self.get_latest_version() |
209 |
||
210 |
if new_version: |
|
211 |
text_version = TextVersion.objects.duplicate(text_version, keep_comments) |
|
| 103 | 212 |
text_version.edit(new_title, new_format, new_content, new_tags, new_note, keep_comments, cancel_modified_scopes) |
| 0 | 213 |
return text_version |
214 |
||
215 |
def __unicode__(self): |
|
216 |
return self.title |
|
217 |
||
218 |
DEFAULT_INPUT_FORMAT = getattr(settings, 'DEFAULT_INPUT_FORMAT', DEFAULT_INPUT_FORMAT_PANDOC) |
|
219 |
CHOICES_INPUT_FORMATS = getattr(settings, 'CHOICES_INPUT_FORMATS', CHOICES_INPUT_FORMATS_PANDOC) |
|
220 |
||
| 24 | 221 |
class TextVersionManager(KeyManager): |
| 0 | 222 |
|
223 |
def duplicate(self, text_version, duplicate_comments=True): |
|
224 |
old_comment_set = set(text_version.comment_set.all()) |
|
225 |
text_version.id = None |
|
| 24 | 226 |
|
227 |
# generate new key |
|
228 |
text_version.key = self._gen_key() |
|
229 |
text_version.adminkey = self._gen_adminkey() |
|
230 |
||
| 0 | 231 |
text_version.save() |
232 |
||
233 |
duplicate_text_version = text_version |
|
234 |
||
235 |
if duplicate_comments: |
|
236 |
old_comment_map = {} |
|
237 |
while len(old_comment_set): |
|
238 |
for c in old_comment_set: |
|
239 |
if not c.reply_to or c.reply_to.id in old_comment_map: |
|
240 |
old_id = c.id |
|
241 |
old_comment_set.remove(c) |
|
242 |
reply_to = None |
|
243 |
if c.reply_to: |
|
244 |
reply_to = old_comment_map[c.reply_to.id] |
|
|
72
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
245 |
c2 = Comment.objects.duplicate(c, duplicate_text_version, reply_to, keep_dates=True) |
| 0 | 246 |
old_comment_map[old_id] = c2 |
247 |
break |
|
248 |
||
249 |
return duplicate_text_version |
|
250 |
||
| 24 | 251 |
class TextVersion(AuthorModel, KeyModel): |
| 0 | 252 |
modified = models.DateTimeField(auto_now=True) |
253 |
created = models.DateTimeField(auto_now_add=True) |
|
254 |
||
255 |
title = models.TextField(ugettext_lazy("Title")) |
|
256 |
format = models.CharField(ugettext_lazy("Format"), max_length=20, blank=False, default=DEFAULT_INPUT_FORMAT, choices=CHOICES_INPUT_FORMATS) |
|
257 |
content = models.TextField(ugettext_lazy("Content")) |
|
258 |
tags = TagField(ugettext_lazy("Tags"), max_length=1000) |
|
259 |
||
260 |
note = models.CharField(ugettext_lazy("Note"), max_length=100, null=True, blank=True) |
|
261 |
||
262 |
mod_posteriori = models.BooleanField(ugettext_lazy('Moderation a posteriori?'), default=True) |
|
263 |
||
|
504
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
264 |
from django.utils.safestring import mark_safe |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
265 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
266 |
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, default=ApplicationConfiguration['workspace_category_1']) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
267 |
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, default=ApplicationConfiguration['workspace_category_2']) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
268 |
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, default=ApplicationConfiguration['workspace_category_3']) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
269 |
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, default=ApplicationConfiguration['workspace_category_4']) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
270 |
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, default=ApplicationConfiguration['workspace_category_5']) |
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
271 |
|
| 0 | 272 |
text = models.ForeignKey("Text") |
273 |
||
274 |
objects = TextVersionManager() |
|
275 |
||
276 |
def get_content(self, format='html'): |
|
|
259
0371caf8bcc6
always use pandoc but in raw mode for html->html convert
raph
parents:
252
diff
changeset
|
277 |
return pandoc_convert(self.content, self.format, format) |
| 478 | 278 |
|
| 0 | 279 |
def get_comments(self): |
280 |
"Warning: data access without security" |
|
281 |
return self.comment_set.filter(reply_to=None, deleted=False) |
|
282 |
||
283 |
def get_replies(self): |
|
284 |
"Warning: data access without security" |
|
285 |
return self.comment_set.filter(~Q(reply_to == None), Q(deleted=False)) |
|
286 |
||
287 |
def __unicode__(self): |
|
288 |
return '<%d> %s' % (self.id, self.title) |
|
289 |
||
| 175 | 290 |
def edit(self, new_title, new_format, new_content, new_tags=None, new_note=None, keep_comments=True, cancel_modified_scopes=True): |
291 |
new_content = on_content_receive(new_content, new_format) |
|
| 0 | 292 |
if not keep_comments : |
293 |
self.comment_set.all().delete() |
|
294 |
elif self.content != new_content or new_format != self.format: |
|
295 |
comments = self.get_comments() ; |
|
296 |
tomodify_comments, toremove_comments = compute_new_comment_positions(self.content, self.format, new_content, new_format, comments) |
|
|
72
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
297 |
[comment.save(keep_dates=True) for comment in tomodify_comments] |
| 103 | 298 |
if cancel_modified_scopes : |
299 |
[comment.remove_scope() for comment in toremove_comments] |
|
300 |
else : |
|
301 |
[comment.delete() for comment in toremove_comments] |
|
302 |
||
| 0 | 303 |
self.title = new_title |
304 |
if new_tags: |
|
305 |
self.tags = new_tags |
|
306 |
if new_note: |
|
307 |
self.note = new_note |
|
308 |
self.content = new_content |
|
309 |
self.format = new_format |
|
310 |
self.save() |
|
| 145 | 311 |
|
312 |
def get_next_version(self): |
|
313 |
other_versions = TextVersion.objects.filter(text__exact=self.text).order_by('created').filter(created__gt=self.created) |
|
314 |
return other_versions[0] if other_versions else None |
|
315 |
if other_versions: |
|
316 |
return |
|
317 |
else: |
|
318 |
return None |
|
319 |
||
320 |
def get_previous_version(self): |
|
321 |
other_versions = TextVersion.objects.filter(text__exact=self.text).order_by('-created').filter(created__lt=self.created) |
|
322 |
return other_versions[0] if other_versions else None |
|
323 |
||
324 |
def get_version_number(self): |
|
325 |
return TextVersion.objects.filter(text__exact=self.text).order_by('created').filter(created__lte=self.created).count() |
|
|
156
6d447220fd1e
bug fix: removed \r in textversion's content because of a comment position bug in pre_edit. Added a save function to TextVersion to do so.
rbernard
parents:
155
diff
changeset
|
326 |
|
| 0 | 327 |
class CommentManager(Manager): |
328 |
||
|
72
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
329 |
def duplicate(self, comment, text_version, reply_to=None, keep_dates=False): |
| 0 | 330 |
comment.id = None |
331 |
comment.text_version = text_version |
|
332 |
if reply_to: |
|
333 |
comment.reply_to = reply_to |
|
334 |
self.update_keys(comment) |
|
|
72
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
335 |
comment.save(keep_dates=keep_dates) |
| 0 | 336 |
return comment |
337 |
||
|
130
f257ad33bed5
model change+migration (add id_key to comment): run ./bin/django migrate
raph
parents:
103
diff
changeset
|
338 |
from cm.models_base import KEY_MAX_SIZE, generate_key |
|
f257ad33bed5
model change+migration (add id_key to comment): run ./bin/django migrate
raph
parents:
103
diff
changeset
|
339 |
|
| 0 | 340 |
class Comment(PermanentModel, AuthorModel): |
|
72
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
341 |
modified = models.DateTimeField() |
|
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
342 |
created = models.DateTimeField() |
| 0 | 343 |
|
|
130
f257ad33bed5
model change+migration (add id_key to comment): run ./bin/django migrate
raph
parents:
103
diff
changeset
|
344 |
# key to identify same comments across versions |
|
f257ad33bed5
model change+migration (add id_key to comment): run ./bin/django migrate
raph
parents:
103
diff
changeset
|
345 |
id_key = models.CharField(max_length=KEY_MAX_SIZE, db_index=True, default=generate_key) |
|
f257ad33bed5
model change+migration (add id_key to comment): run ./bin/django migrate
raph
parents:
103
diff
changeset
|
346 |
|
| 0 | 347 |
text_version = models.ForeignKey("TextVersion") |
348 |
||
349 |
# comment_set will be replies |
|
350 |
reply_to = models.ForeignKey("Comment", null=True, blank=True) |
|
351 |
||
352 |
title = models.TextField() |
|
353 |
content = models.TextField() |
|
354 |
content_html = models.TextField() |
|
355 |
||
356 |
format = models.CharField(_("Format:"), max_length=20, blank=False, default=DEFAULT_INPUT_FORMAT, choices=CHOICES_INPUT_FORMATS) |
|
357 |
||
358 |
tags = TagField() |
|
|
504
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
359 |
|
|
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
360 |
category = models.PositiveSmallIntegerField(default=0) |
| 0 | 361 |
|
362 |
start_wrapper = models.IntegerField(null=True, blank=True) |
|
363 |
end_wrapper = models.IntegerField(null=True, blank=True) |
|
364 |
start_offset = models.IntegerField(null=True, blank=True) |
|
365 |
end_offset = models.IntegerField(null=True, blank=True) |
|
366 |
||
367 |
objects = CommentManager() |
|
368 |
||
| 327 | 369 |
def save(self, keep_dates=False, **kwargs): |
|
72
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
370 |
if not keep_dates: |
|
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
371 |
now = datetime.now() |
|
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
372 |
if not self.id: |
|
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
373 |
self.created = now |
|
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
374 |
self.modified = now |
| 327 | 375 |
super(PermanentModel, self).save(**kwargs) |
|
72
b0c777412d0a
keep comment dates (created / modified) when duplicating (fixes #12)
raph
parents:
38
diff
changeset
|
376 |
|
| 0 | 377 |
def __unicode__(self): |
| 262 | 378 |
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, ) |
| 0 | 379 |
|
380 |
def is_reply(self): |
|
381 |
return self.reply_to != None |
|
382 |
||
| 16 | 383 |
def is_thread_full_visible(self, own_user=None): |
384 |
""" |
|
385 |
own_user: comment belonging to this user are also visible |
|
386 |
""" |
|
387 |
if self.state == 'approved' or (own_user and self.user == own_user): |
|
388 |
if self.reply_to==None: |
|
389 |
return True |
|
390 |
else: |
|
391 |
return self.reply_to.is_thread_full_visible(own_user) |
|
392 |
return False |
|
393 |
||
| 103 | 394 |
def is_scope_removed(self): |
395 |
#when scope is "removed" we will have |
|
396 |
#self.start_wrapper == self.end_wrapper == self.start_offset == self.end_offset == -1 |
|
397 |
return (self.start_wrapper == -1) |
|
398 |
||
| 0 | 399 |
def top_comment(self): |
400 |
if self.reply_to == None : |
|
401 |
return self |
|
402 |
else : |
|
403 |
return self.reply_to.top_comment() |
|
404 |
||
405 |
def depth(self): |
|
406 |
if self.reply_to == None : |
|
407 |
return 0 |
|
408 |
else : |
|
409 |
return 1 + self.reply_to.depth() |
|
410 |
||
411 |
def delete(self): |
|
412 |
PermanentModel.delete(self) |
|
413 |
# delete replies |
|
414 |
[c.delete() for c in self.comment_set.all()] |
|
| 103 | 415 |
|
416 |
def remove_scope(self): |
|
417 |
self.start_wrapper = self.end_wrapper = self.start_offset = self.end_offset = -1 |
|
418 |
self.save() |
|
419 |
||
| 0 | 420 |
# http://docs.djangoproject.com/en/dev/topics/files/#topics-files |
421 |
||
422 |
class AttachmentManager(KeyManager): |
|
423 |
def create_attachment(self, text_version, filename, data): |
|
424 |
attach = self.create(text_version=text_version) |
|
425 |
ff = ContentFile(data) |
|
426 |
attach.data.save(filename, ff) |
|
427 |
return attach |
|
428 |
||
429 |
class Attachment(KeyModel): |
|
430 |
data = models.FileField(upload_to="attachments/%Y/%m/%d/", max_length=1000) |
|
|
504
b2e0186daa5b
Adds a category to comments, painted with colored vertical bar.
gibus
parents:
478
diff
changeset
|
431 |
text_version = models.ForeignKey(TextVersion, null=True, blank=True, default=None) |
| 0 | 432 |
|
433 |
objects = AttachmentManager() |
|
434 |
||
435 |
class NotificationManager(KeyManager): |
|
| 12 | 436 |
def create_notification(self, text, type, active, email_or_user): |
437 |
notification = self.create(text=text, type=type, active=active) |
|
438 |
notification.set_email_or_user(email_or_user) |
|
439 |
return notification |
|
| 0 | 440 |
|
| 12 | 441 |
def get_notifications(self, text, type, email_or_user): |
| 0 | 442 |
if isinstance(email_or_user,unicode): |
443 |
prev_notifications = Notification.objects.filter(text=text, type=type, email=email_or_user) |
|
444 |
else: |
|
445 |
prev_notifications = Notification.objects.filter(text=text, type=type, user=email_or_user) |
|
| 12 | 446 |
|
| 0 | 447 |
if prev_notifications: |
448 |
return prev_notifications[0] |
|
449 |
else: |
|
450 |
return None |
|
451 |
||
| 12 | 452 |
def set_notification(self, text, type, active, email_or_user): |
453 |
notification = self.get_notifications(text, type, email_or_user) |
|
454 |
if notification == None : |
|
455 |
self.create_notification(text, type, active, email_or_user) |
|
456 |
else : |
|
457 |
notification.active = active |
|
| 0 | 458 |
notification.save() |
459 |
||
460 |
class Notification(KeyModel, AuthorModel): |
|
461 |
text = models.ForeignKey(Text, null=True, blank=True) |
|
462 |
type = models.CharField(max_length=30, null=True, blank=True) |
|
463 |
active = models.BooleanField(default=True) # active = False means user desactivation |
|
464 |
||
465 |
objects = NotificationManager() |
|
466 |
||
467 |
def desactivate_notification_url(self): |
|
468 |
return reverse('desactivate-notification', args=[self.adminkey]) |
|
469 |
||
470 |
def desactivate(self): |
|
471 |
if self.type=='own': |
|
472 |
self.active = False |
|
473 |
self.save() |
|
474 |
else: |
|
475 |
self.delete() |
|
476 |
||
| 266 | 477 |
def __unicode__(self): |
478 |
return u"%s: %s %s %s %s" % (self.__class__.__name__, self.user, self.text, self.type, self.active ) |
|
479 |
||
| 0 | 480 |
# right management |
481 |
class UserRoleManager(models.Manager): |
|
482 |
def create_userroles_text(self, text): |
|
483 |
# make sure every user has a userrole on this text |
|
484 |
for user in User.objects.all(): |
|
485 |
userrole, _ = self.get_or_create(user=user, text=text) |
|
486 |
# anon user |
|
487 |
userrole, _ = self.get_or_create(user=None, text=text) |
|
488 |
# anon global user |
|
489 |
global_userrole, _ = self.get_or_create(user=None, text=None) |
|
490 |
||
491 |
class UserRole(models.Model): |
|
492 |
role = models.ForeignKey("Role", null=True, blank=True) |
|
493 |
||
494 |
# user == null => anyone |
|
495 |
user = models.ForeignKey(User, null=True, blank=True) |
|
496 |
||
497 |
# text == null => any text (workspace role) |
|
498 |
text = models.ForeignKey(Text, null=True, blank=True) |
|
499 |
||
500 |
objects = UserRoleManager() |
|
501 |
||
502 |
class Meta: |
|
503 |
unique_together = (('role', 'user', 'text',)) |
|
504 |
||
505 |
def __unicode__(self): |
|
506 |
if self.role: |
|
507 |
rolename = _(self.role.name) |
|
508 |
else: |
|
509 |
rolename = '' |
|
510 |
||
511 |
if self.user: |
|
512 |
return u"%s: %s %s %s" % (self.__class__.__name__, self.user.username, self.text, rolename) |
|
513 |
else: |
|
514 |
return u"%s: *ALL* %s %s" % (self.__class__.__name__, self.text, rolename) |
|
515 |
||
516 |
def __repr__(self): |
|
517 |
return self.__unicode__() |
|
518 |
||
519 |
from cm.models_base import generate_key |
|
520 |
from cm.utils.misc import update |
|
521 |
||
522 |
class Role(models.Model): |
|
523 |
""" |
|
524 |
'Static' application roles |
|
525 |
""" |
|
526 |
name = models.CharField(ugettext_lazy('name'), max_length=50, unique=True) |
|
527 |
description = models.TextField(ugettext_lazy('description')) |
|
528 |
permissions = models.ManyToManyField(Permission, related_name="roles") |
|
529 |
||
530 |
global_scope = models.BooleanField('global scope', default=False) # applies to global scope only |
|
531 |
anon = models.BooleanField('anonymous', default=False) # role possible for anonymous users? |
|
532 |
||
533 |
def __unicode__(self): |
|
534 |
return _(self.name) |
|
535 |
||
536 |
def __hash__(self): |
|
537 |
return self.id |
|
538 |
||
539 |
def name_i18n(self): |
|
540 |
return _(self.name) |
|
541 |
||
542 |
from django.utils.safestring import mark_safe |
|
543 |
||
544 |
class RegistrationManager(KeyManager): |
|
545 |
def activate_user(self, activation_key): |
|
546 |
""" |
|
547 |
Validates an activation key and activates the corresponding |
|
548 |
``User`` if valid. |
|
549 |
If the key is valid , returns the ``User`` as second arg |
|
550 |
First is boolean indicating if user has just been activated |
|
551 |
""" |
|
552 |
# Make sure the key we're trying conforms to the pattern of a |
|
553 |
# SHA1 hash; if it doesn't, no point trying to look it up in |
|
554 |
# the database. |
|
555 |
try: |
|
556 |
profile = self.get(admin_key=activation_key) |
|
557 |
except self.model.DoesNotExist: |
|
558 |
return False, False |
|
559 |
user = profile.user |
|
560 |
activated = False |
|
561 |
if not user.is_active: |
|
562 |
user.is_active = True |
|
563 |
user.save() |
|
564 |
activated = True |
|
565 |
return (activated, user) |
|
566 |
||
567 |
def _create_manager(self, email, username, password, first_name, last_name): |
|
568 |
if username and email and password and len(User.objects.filter(username=username)) == 0: |
|
569 |
user = User.objects.create(username=username, email=email, first_name=first_name, last_name=last_name, is_active=True) |
|
570 |
user.set_password(password) |
|
571 |
user.save() |
|
572 |
||
573 |
profile = UserProfile.objects.create(user=user) |
|
574 |
||
575 |
manager = Role.objects.get(name='Manager') |
|
576 |
UserRole.objects.create(text=None, user=user, role=manager) |
|
577 |
return user |
|
578 |
else: |
|
579 |
return None |
|
580 |
||
581 |
||
582 |
def create_inactive_user(self, email, send_invitation, **kwargs): |
|
|
448
054d572a3db4
Avoid warnings for deprecated instructions in djan 1.3
gibus
parents:
407
diff
changeset
|
583 |
if 'postgresql' in settings.DATABASES['default']['ENGINE']: |
|
305
df2c3202fd8e
do not lock table with backends other than postgresql (postgresql specific syntax) (fixes #52)
raph
parents:
289
diff
changeset
|
584 |
#prevent concurrent access |
|
df2c3202fd8e
do not lock table with backends other than postgresql (postgresql specific syntax) (fixes #52)
raph
parents:
289
diff
changeset
|
585 |
cursor = connection.cursor() |
|
df2c3202fd8e
do not lock table with backends other than postgresql (postgresql specific syntax) (fixes #52)
raph
parents:
289
diff
changeset
|
586 |
sql = "LOCK TABLE auth_user IN EXCLUSIVE MODE" |
|
df2c3202fd8e
do not lock table with backends other than postgresql (postgresql specific syntax) (fixes #52)
raph
parents:
289
diff
changeset
|
587 |
cursor.execute(sql) |
| 0 | 588 |
|
589 |
try: |
|
590 |
user_with_email = User.objects.get(email__iexact=email) |
|
591 |
except User.DoesNotExist: |
|
592 |
user = User.objects.create(username=email, email=email) |
|
593 |
profile = UserProfile.objects.create(user=user) |
|
594 |
update(user, kwargs) |
|
595 |
update(profile, kwargs) |
|
596 |
||
597 |
user.is_active = False |
|
598 |
user.save() |
|
599 |
profile.save() |
|
600 |
||
601 |
note = kwargs.get('note', None) |
|
602 |
if send_invitation: |
|
603 |
profile.send_activation_email(note) |
|
604 |
return user |
|
605 |
else: |
|
606 |
return user_with_email |
|
607 |
||
608 |
||
609 |
from cm.utils.mail import send_mail |
|
610 |
||
611 |
class UserProfile(KeyModel): |
|
612 |
modified = models.DateTimeField(auto_now=True) |
|
613 |
created = models.DateTimeField(auto_now_add=True) |
|
614 |
||
615 |
user = models.ForeignKey(User, unique=True) |
|
616 |
||
617 |
allow_contact = models.BooleanField(ugettext_lazy(u'Allow contact'), default=True, help_text=ugettext_lazy(u"Allow email messages from other users")) |
|
618 |
preferred_language = models.CharField(ugettext_lazy(u'Preferred language'), max_length=2, default="en") |
|
619 |
is_temp = models.BooleanField(default=False) |
|
620 |
is_email_error = models.BooleanField(default=False) |
|
621 |
is_suspended = models.BooleanField(ugettext_lazy(u'Suspended access'), default=False) # used to disable access or to wait for approval when registering |
|
622 |
||
| 38 | 623 |
tags = TagField(ugettext_lazy("Tags"), max_length=1000) |
624 |
||
| 0 | 625 |
objects = RegistrationManager() |
626 |
||
627 |
class Meta: |
|
628 |
permissions = ( |
|
629 |
("can_create_user", "Can create user"), |
|
630 |
("can_delete_user", "Can delete user"), |
|
631 |
) |
|
632 |
||
633 |
def __unicode__(self): |
|
634 |
return unicode(self.user) |
|
635 |
||
636 |
def global_userrole(self): |
|
637 |
try: |
|
638 |
return UserRole.objects.get(user=self.user, text=None) |
|
639 |
except UserRole.DoesNotExist: |
|
640 |
return None |
|
641 |
||
642 |
def admin_print(self): |
|
643 |
if self.is_suspended: |
|
644 |
if self.user.is_active: |
|
645 |
return mark_safe('%s (%s)' % (self.user.username, _(u'suspended'),)) |
|
646 |
else: |
|
647 |
return mark_safe('%s (%s)' % (self.user.username, _(u'waiting approval'),)) |
|
648 |
else: |
|
649 |
if self.user.is_active: |
|
650 |
return mark_safe('%s' % self.user.username) |
|
651 |
else: |
|
652 |
email_username = self.user.email.split('@')[0] |
|
653 |
return mark_safe('%s (%s)' % (self.user.username, _(u'pending'),)) |
|
654 |
||
655 |
def simple_print(self): |
|
656 |
if self.user.is_active: |
|
657 |
return self.user.username |
|
658 |
else: |
|
659 |
return self.user.email |
|
660 |
||
661 |
def send_activation_email(self, note=None): |
|
662 |
self._send_act_invit_email(note=note, template='email/activation_email.txt') |
|
663 |
||
664 |
def send_invitation_email(self, note=None): |
|
665 |
self._send_act_invit_email(note=note, template='email/invitation_email.txt') |
|
666 |
||
667 |
def _send_act_invit_email(self, template, note=None): |
|
668 |
subject = _(u'Invitation') |
|
669 |
||
670 |
activate_url = reverse('user-activate', args=[self.adminkey]) |
|
671 |
message = render_to_string(template, |
|
672 |
{ |
|
673 |
'activate_url' : activate_url, |
|
674 |
'note' : note, |
|
675 |
'CONF': ApplicationConfiguration |
|
676 |
}) |
|
677 |
||
678 |
send_mail(subject, message, ApplicationConfiguration['email_from'], [self.user.email]) |
|
679 |
||
680 |
from django.db.models import signals |
|
681 |
||
682 |
def delete_profile(sender, **kwargs): |
|
683 |
user_profile = kwargs['instance'] |
|
684 |
user = user_profile.user |
|
685 |
user.delete() |
|
686 |
||
687 |
signals.post_delete.connect(delete_profile, sender=UserProfile) |
|
688 |
||
689 |
class ActivityManager(models.Manager): |
|
690 |
pass |
|
691 |
||
692 |
class Activity(models.Model): |
|
693 |
created = models.DateTimeField(auto_now_add=True) |
|
694 |
originator_user = models.ForeignKey(User, related_name='originator_activity', null=True, blank=True, default=None) |
|
695 |
text = models.ForeignKey(Text, null=True, blank=True, default=None) |
|
696 |
text_version = models.ForeignKey(TextVersion, null=True, blank=True, default=None) |
|
697 |
comment = models.ForeignKey(Comment, null=True, blank=True, default=None) |
|
698 |
user = models.ForeignKey(User, null=True, blank=True, default=None) |
|
699 |
type = models.CharField(max_length=30) |
|
700 |
ip = models.IPAddressField(null=True, blank=True, default=None) |
|
701 |
||
702 |
objects = ActivityManager() |
|
703 |
||
704 |
# viewable activities (i.e. now 'text-view') |
|
705 |
VIEWABLE_ACTIVITIES = { |
|
706 |
'view_comments' : ['comment_created', 'comment_removed'], |
|
707 |
'view_users' : ['user_created', 'user_activated', 'user_refused', 'user_enabled', 'user_approved', 'user_suspended'], |
|
|
460
2fdb7d095d5c
Added import from XML file, including text, comments and attachments.
gibus
parents:
448
diff
changeset
|
708 |
'view_texts' : ['text_created', 'text_imported', 'text_removed', 'text_edited', 'text_edited_new_version'], |
| 0 | 709 |
} |
710 |
ACTIVITIES_TYPES = reduce(list.__add__, VIEWABLE_ACTIVITIES.values()) |
|
711 |
||
712 |
IMGS = { |
|
713 |
'text_created' : u'page_add_small.png', |
|
|
460
2fdb7d095d5c
Added import from XML file, including text, comments and attachments.
gibus
parents:
448
diff
changeset
|
714 |
'text_imported' : u'sop_import_small.png', |
| 0 | 715 |
'text_removed' : u'page_delete_small.png', |
716 |
'text_edited' : u'page_save_small.png', |
|
717 |
'text_edited_new_version' : u'page_save_small.png', |
|
718 |
'user_created' : u'user_add_small.png', |
|
719 |
'user_enabled' : u'user_add_small.png', |
|
720 |
'user_refused': u'user_delete_small.png', |
|
721 |
'user_suspended': u'user_delete_small.png', |
|
722 |
'user_approved': u'user_add_small.png', |
|
723 |
'user_activated' : u'user_go.png', |
|
724 |
'comment_created' : u'note_add_small.png', |
|
725 |
'comment_removed' : u'note_delete_small.png', |
|
726 |
} |
|
727 |
||
728 |
#type/msg |
|
729 |
MSGS = { |
|
| 227 | 730 |
'text_edited' : ugettext_lazy(u'Text %(link_to_text)s edited by %(creator)s'), |
731 |
'text_edited_new_version' : ugettext_lazy(u'Text %(link_to_text)s edited (new version created) by %(creator)s'), |
|
732 |
'text_created' : ugettext_lazy(u'Text %(link_to_text)s added by %(creator)s'), |
|
|
460
2fdb7d095d5c
Added import from XML file, including text, comments and attachments.
gibus
parents:
448
diff
changeset
|
733 |
'text_imported' : ugettext_lazy(u'Text %(link_to_text)s imported by %(creator)s'), |
| 5 | 734 |
'text_removed' : ugettext_lazy(u'Text %(link_to_text)s removed'), |
| 227 | 735 |
'comment_created' : ugettext_lazy(u'Comment %(link_to_comment)s added on text %(link_to_text)s by %(creator)s'), |
| 5 | 736 |
'comment_removed' : ugettext_lazy(u'Comment %(link_to_comment)s removed from text %(link_to_text)s'), |
737 |
'user_created' : ugettext_lazy(u'User %(username)s added'), |
|
738 |
'user_enabled' : ugettext_lazy(u'User %(username)s access to workspace enabled'), |
|
739 |
'user_refused' : ugettext_lazy(u'User %(username)s access to workspace refused'), |
|
740 |
'user_suspended' : ugettext_lazy(u'User %(username)s access to workspace suspended'), |
|
741 |
'user_activated' : ugettext_lazy(u'User %(username)s access to workspace activated'), |
|
742 |
'user_approved' : ugettext_lazy(u'User %(username)s has activated his account'), |
|
| 0 | 743 |
} |
744 |
||
745 |
def is_same_user(self, other_activity): |
|
746 |
if (self.originator_user != None or other_activity.originator_user != None) and self.originator_user != other_activity.originator_user: |
|
747 |
return False |
|
748 |
else: |
|
749 |
return self.ip != None and self.ip == other_activity.ip |
|
750 |
||
751 |
def linkable_text_title(self, html=True, link=True): |
|
752 |
# html: whether or not output sould be html |
|
753 |
format_args = {'link':absolute_reverse('text-view', args=[self.text.key]), 'title':self.text.title} |
|
754 |
if html and not self.text.deleted : |
|
755 |
return mark_safe(u'<a href="%(link)s">%(title)s</a>' % format_args) |
|
756 |
else: |
|
757 |
if link and not self.text.deleted: |
|
758 |
return u'%(title)s (%(link)s)' % format_args |
|
759 |
else: |
|
760 |
return self.text.title ; |
|
761 |
||
762 |
def linkable_comment_title(self, html=True, link=True): |
|
763 |
if self.comment: |
|
| 144 | 764 |
format_args = {'link':absolute_reverse('text-view-show-comment', args=[self.text.key, self.comment.id_key]), 'title':self.comment.title} |
| 0 | 765 |
if html and not self.comment.deleted and not self.text.deleted: |
766 |
return mark_safe(u'<a href="%(link)s">%(title)s</a>' % format_args) |
|
767 |
else : |
|
768 |
if link and not self.comment.deleted and not self.text.deleted: |
|
769 |
return u'%(title)s (%(link)s)' % format_args |
|
770 |
else: |
|
771 |
return self.comment.title ; |
|
772 |
else: |
|
773 |
return u'' |
|
774 |
||
775 |
def __unicode__(self): |
|
776 |
return u"%s %s %s %s %s" % (self.type, self.originator_user, self.text, self.comment, self.user) |
|
777 |
||
778 |
def img_name(self): |
|
779 |
return self.IMGS.get(self.type) |
|
780 |
||
781 |
def printable_data_nohtml_link(self): |
|
782 |
return self.printable_data(html=False, link=True) |
|
783 |
||
784 |
def printable_data(self, html=True, link=True): |
|
785 |
msg = self.MSGS.get(self.type, None) |
|
786 |
if msg: |
|
787 |
return mark_safe(msg % { |
|
788 |
'link_to_text' : self.linkable_text_title(html=html, link=link) if self.text else None, |
|
789 |
'link_to_comment' : self.linkable_comment_title(html=html, link=link) if self.comment else None, |
|
790 |
'username' : self.user.username if self.user else None, |
|
| 227 | 791 |
'creator' : self.originator_user.username if self.originator_user else ugettext_lazy(u'anonymous'), |
| 0 | 792 |
}) |
793 |
return '' |
|
794 |
||
795 |
def printable_metadata(self, html=True): |
|
796 |
ret = [] |
|
797 |
if self.type == 'user_activated': |
|
798 |
ret.append(_(u'by "%(username)s"') % {'username' : self.originator_user.username}) |
|
799 |
ret.append(' ') |
|
800 |
ret.append(_(u"%(time_since)s ago") % {'time_since':timesince(self.created)}) |
|
801 |
return ''.join(ret) |
|
802 |
||
803 |
def printable_metadata_absolute(self, html=True): |
|
804 |
ret = [] |
|
805 |
if self.type == 'user_activated': |
|
806 |
ret.append(_(u'by "%(username)s"') % {'username' : self.originator_user.username}) |
|
807 |
ret.append(u' ') |
|
808 |
ret.append(datetime_to_user_str(self.created)) |
|
809 |
return u''.join(ret) |
|
810 |
||
811 |
import cm.denorm_engine |
|
812 |
import cm.admin |
|
813 |
import cm.main |
|
814 |
import cm.activity |
|
815 |
import cm.notifications |
|
816 |
||
817 |
# we fill username with email so we need a bigger value |
|
818 |
User._meta.get_field('username').max_length = 75 |
|
|
347
0a359382701b
prevent uno custom __import__ from messing with south import machinery (to discover south enabled dj apps)
Production Moz <dev@sopinspace.com>
parents:
327
diff
changeset
|
819 |
|
|
0a359382701b
prevent uno custom __import__ from messing with south import machinery (to discover south enabled dj apps)
Production Moz <dev@sopinspace.com>
parents:
327
diff
changeset
|
820 |
import monkey_patches |