1 from django.utils.translation import ugettext as _
\r
2 from utils import PickledObjectField
\r
3 from threading import Thread
\r
7 class ActionQuerySet(CachedQuerySet):
\r
8 def obj_from_datadict(self, datadict):
\r
9 cls = ActionProxyMetaClass.types.get(datadict['action_type'], None)
\r
12 obj.__dict__.update(datadict)
\r
15 return super(ActionQuerySet, self).obj_from_datadict(datadict)
\r
17 def get(self, *args, **kwargs):
\r
18 return super(ActionQuerySet, self).get(*args, **kwargs).leaf()
\r
20 class ActionManager(CachedManager):
\r
21 use_for_related_fields = True
\r
23 def get_query_set(self):
\r
24 qs = ActionQuerySet(self.model)
\r
26 if self.model is not Action:
\r
27 return qs.filter(action_type=self.model.get_type())
\r
31 def get_for_types(self, types, *args, **kwargs):
\r
32 kwargs['action_type__in'] = [t.get_type() for t in types]
\r
33 return self.get(*args, **kwargs)
\r
36 class Action(BaseModel):
\r
37 user = models.ForeignKey('User', related_name="actions")
\r
38 ip = models.CharField(max_length=16)
\r
39 node = models.ForeignKey('Node', null=True, related_name="actions")
\r
40 action_type = models.CharField(max_length=16)
\r
41 action_date = models.DateTimeField(default=datetime.datetime.now)
\r
43 extra = PickledObjectField()
\r
45 canceled = models.BooleanField(default=False)
\r
46 canceled_by = models.ForeignKey('User', null=True, related_name="canceled_actions")
\r
47 canceled_at = models.DateTimeField(null=True)
\r
48 canceled_ip = models.CharField(max_length=16)
\r
52 objects = ActionManager()
\r
56 return self.action_date
\r
62 def repute_users(self):
\r
65 def process_data(self, **data):
\r
68 def process_action(self):
\r
71 def cancel_action(self):
\r
78 def describe(self, viewer=None):
\r
79 return self.__class__.__name__
\r
81 def get_absolute_url(self):
\r
83 return self.node.get_absolute_url()
\r
85 return self.user.get_profile_url()
\r
87 def repute(self, user, value):
\r
88 repute = ActionRepute(action=self, user=user, value=value)
\r
92 def cancel_reputes(self):
\r
93 for repute in self.reputes.all():
\r
94 cancel = ActionRepute(action=self, user=repute.user, value=(-repute.value), by_canceled=True)
\r
98 leaf_cls = ActionProxyMetaClass.types.get(self.action_type, None)
\r
100 if leaf_cls is None:
\r
104 d = self._as_dict()
\r
105 leaf.__dict__.update(self._as_dict())
\r
106 l = leaf._as_dict()
\r
111 return re.sub(r'action$', '', cls.__name__.lower())
\r
113 def save(self, data=None, *args, **kwargs):
\r
117 self.action_type = self.__class__.get_type()
\r
121 self.process_data(**data)
\r
123 super(Action, self).save(*args, **kwargs)
\r
126 if (self.node is None) or (not self.node.nis.wiki):
\r
127 self.repute_users()
\r
128 self.process_action()
\r
129 self.trigger_hooks(True)
\r
133 def delete(self, *args, **kwargs):
\r
134 self.cancel_action()
\r
135 super(Action, self).delete(*args, **kwargs)
\r
137 def cancel(self, user=None, ip=None):
\r
138 if not self.canceled:
\r
139 self.canceled = True
\r
140 self.canceled_at = datetime.datetime.now()
\r
141 self.canceled_by = (user is None) and self.user or user
\r
143 self.canceled_ip = ip
\r
145 self.cancel_reputes()
\r
146 self.cancel_action()
\r
147 #self.trigger_hooks(False)
\r
150 def get_current(cls, **kwargs):
\r
151 kwargs['canceled'] = False
\r
154 return cls.objects.get(**kwargs)
\r
155 except cls.MultipleObjectsReturned:
\r
156 logging.error("Got multiple values for action %s with args %s", cls.__name__,
\r
157 ", ".join(["%s='%s'" % i for i in kwargs.items()]))
\r
159 except cls.DoesNotExist:
\r
164 if not Action.hooks.get(cls, None):
\r
165 Action.hooks[cls] = []
\r
167 Action.hooks[cls].append(fn)
\r
169 def trigger_hooks(self, new=True):
\r
170 thread = Thread(target=trigger_hooks_threaded, args=[self, Action.hooks, new])
\r
171 thread.setDaemon(True)
\r
175 app_label = 'forum'
\r
177 def trigger_hooks_threaded(action, hooks, new):
\r
178 for cls, hooklist in hooks.items():
\r
179 if isinstance(action, cls):
\r
180 for hook in hooklist:
\r
182 hook(action=action, new=new)
\r
183 except Exception, e:
\r
184 logging.error("Error in %s hook: %s" % (cls.__name__, str(e)))
\r
186 class ActionProxyMetaClass(BaseMetaClass):
\r
189 def __new__(cls, *args, **kwargs):
\r
190 new_cls = super(ActionProxyMetaClass, cls).__new__(cls, *args, **kwargs)
\r
191 cls.types[new_cls.get_type()] = new_cls
\r
196 new_cls.Meta = Meta
\r
199 class ActionProxy(Action):
\r
200 __metaclass__ = ActionProxyMetaClass
\r
202 def friendly_username(self, viewer, user):
\r
203 return (viewer == user) and _('You') or user.username
\r
205 def friendly_ownername(self, owner, user):
\r
206 return (owner == user) and _('your') or user.username
\r
208 def viewer_or_user_verb(self, viewer, user, viewer_verb, user_verb):
\r
209 return (viewer == user) and viewer_verb or user_verb
\r
211 def hyperlink(self, url, title, **attrs):
\r
212 return '<a href="%s" %s>%s</a>' % (url, " ".join('%s="%s"' % i for i in attrs.items()), title)
\r
214 def describe_node(self, viewer, node):
\r
215 node_link = self.hyperlink(node.get_absolute_url(), node.headline)
\r
218 node_desc = _("on %(link)s") % {'link': node_link}
\r
220 node_desc = node_link
\r
222 return _("%(user)s %(node_name)s %(node_desc)s") % {
\r
223 'user': self.hyperlink(node.author.get_profile_url(), self.friendly_ownername(viewer, node.author)),
\r
224 'node_name': node.friendly_name, 'node_desc': node_desc,
\r
230 class DummyActionProxyMetaClass(type):
\r
231 def __new__(cls, *args, **kwargs):
\r
232 new_cls = super(DummyActionProxyMetaClass, cls).__new__(cls, *args, **kwargs)
\r
233 ActionProxyMetaClass.types[new_cls.get_type()] = new_cls
\r
236 class DummyActionProxy(object):
\r
237 __metaclass__ = DummyActionProxyMetaClass
\r
241 def __init__(self, ip=None):
\r
244 def process_data(self, **data):
\r
247 def process_action(self):
\r
250 def save(self, data=None):
\r
251 self.process_action()
\r
254 self.process_data(**data)
\r
256 for hook in self.__class__.hooks:
\r
261 return re.sub(r'action$', '', cls.__name__.lower())
\r
265 cls.hooks.append(fn)
\r
269 class ActionRepute(models.Model):
\r
270 action = models.ForeignKey(Action, related_name='reputes')
\r
271 date = models.DateTimeField(default=datetime.datetime.now)
\r
272 user = models.ForeignKey('User', related_name='reputes')
\r
273 value = models.IntegerField(default=0)
\r
274 by_canceled = models.BooleanField(default=False)
\r
277 def positive(self):
\r
278 if self.value > 0: return self.value
\r
282 def negative(self):
\r
283 if self.value < 0: return self.value
\r
286 def save(self, *args, **kwargs):
\r
287 super(ActionRepute, self).save(*args, **kwargs)
\r
288 self.user.reputation += self.value
\r
292 self.user.reputation -= self.value
\r
294 super(ActionRepute, self).delete()
\r
297 app_label = 'forum'
\r