summaryrefslogtreecommitdiffstats
path: root/forum/models
diff options
context:
space:
mode:
Diffstat (limited to 'forum/models')
-rw-r--r--forum/models/__init__.py92
-rw-r--r--forum/models/answer.py28
-rw-r--r--forum/models/base.py158
-rw-r--r--forum/models/content.py22
-rw-r--r--forum/models/meta.py34
-rw-r--r--forum/models/question.py78
-rw-r--r--forum/models/tag.py4
-rw-r--r--forum/models/user.py29
8 files changed, 255 insertions, 190 deletions
diff --git a/forum/models/__init__.py b/forum/models/__init__.py
index 3b4fd8c0..2df44bcc 100644
--- a/forum/models/__init__.py
+++ b/forum/models/__init__.py
@@ -1,11 +1,7 @@
-import signals
-from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion, FavoriteQuestion
-from answer import Answer, AnonymousAnswer, AnswerRevision
-from tag import Tag, MarkedTag
-from meta import Vote, Comment, FlaggedItem
-from user import Activity, ValidationHash, EmailFeedSetting
-#from user import AuthKeyUserAssociation
-from repute import Badge, Award, Repute
+import logging
+import re
+import hashlib
+import datetime
from django.core.urlresolvers import reverse
from django.core.mail import EmailMessage
from forum.search.indexer import create_fulltext_indexes
@@ -17,15 +13,19 @@ from django.template.defaultfilters import slugify
from django.utils.safestring import mark_safe
from django.db import models
from django.conf import settings as django_settings
-from forum import const
-import logging
-import re
-import hashlib
-
-import datetime
from django.contrib.contenttypes.models import ContentType
-
-#todo: must go after signals
+from forum import const
+from forum.models.question import Question, QuestionRevision
+from forum.models.question import QuestionView, AnonymousQuestion
+from forum.models.question import FavoriteQuestion
+from forum.models.answer import Answer, AnonymousAnswer, AnswerRevision
+from forum.models.tag import Tag, MarkedTag
+from forum.models.meta import Vote, Comment, FlaggedItem
+from forum.models.user import Activity, ValidationHash, EmailFeedSetting
+from forum.models import signals
+#from user import AuthKeyUserAssociation
+from forum.models.repute import Badge, Award, Repute
+from forum.conf import settings as forum_settings
from forum import auth
User.add_to_class('is_approved', models.BooleanField(default=False))
@@ -186,7 +186,7 @@ def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None):
if cancel:
auth.onDownVotedCanceled(vote, post, user, timestamp)
else:
- auth.onDonwVoted(vote, post, user, timestamp)
+ auth.onDownVoted(vote, post, user, timestamp)
def upvote(self, post, timestamp=None, cancel=False):
_process_vote(
@@ -210,14 +210,14 @@ def accept_answer(self, answer, timestamp=None, cancel=False):
else:
auth.onAnswerAccept(answer, self, timestamp=timestamp)
-def flag_post(self, post, timestamp=None, cancel=False):
+def flag_post(user, post, timestamp=None, cancel=False):
if cancel:#todo: can't unflag?
return
if post.flagged_items.filter(user=user).count() > 0:
return
else:
flag = FlaggedItem(
- user = self,
+ user = user,
content_object = post,
flagged_at = timestamp,
)
@@ -296,7 +296,7 @@ def send_instant_notifications_about_activity_in_post(
#get details about update
#todo: is there a way to solve this import issue?
- from forum.conf import settings as forum_settings
+ #from forum.conf import settings as forum_settings
base_url = forum_settings.APP_URL
data = {
'receiving_user': u,
@@ -313,7 +313,8 @@ def send_instant_notifications_about_activity_in_post(
}
#send update
subject = _('email update message subject')
- text = format_instant_notification_body(template, data)
+ text = format_instant_notification_body(
+ )
msg = EmailMessage(subject, text, django_settings.DEFAULT_FROM_EMAIL, [u.email])
print 'sending email to %s' % u.email
print 'subject: %s' % subject
@@ -337,29 +338,8 @@ def record_ask_event(instance, created, **kwargs):
)
activity.save()
-#todo: translate this
-record_answer_event_re = re.compile("You have received (a|\d+) .*new response.*")
def record_answer_event(instance, created, **kwargs):
if created:
- q_author = instance.question.author
- found_match = False
- for m in q_author.message_set.all():
- match = record_answer_event_re.search(m.message)
- if match:
- found_match = True
- try:
- cnt = int(match.group(1))
- except:
- cnt = 1
- m.message = u"You have received %d <a href=\"%s?sort=responses\">new responses</a>."\
- % (cnt+1, q_author.get_profile_url())
- m.save()
- break
- if not found_match:
- msg = u"You have received a <a href=\"%s?sort=responses\">new response</a>."\
- % q_author.get_profile_url()
- q_author.message_set.create(message=msg)
-
activity = Activity(
user = instance.author,
active_at = instance.added_at,
@@ -376,15 +356,26 @@ def record_answer_event(instance, created, **kwargs):
#todo: change to more general post_update_activity
-def record_post_update_activity(post, newly_mentioned_users, **kwargs):
+def record_post_update_activity(
+ post,
+ newly_mentioned_users = list(),
+ timestamp = None,
+ created = False,
+ **kwargs
+ ):
+ """called upon signal forum.models.signals.post_updated
+ which is sent at the end of save() method in posts
+ """
#todo: take into account created == True case
- activity_type = post.get_updated_activity_type()
+ activity_type = post.get_updated_activity_type(created)
+
+ assert(timestamp != None)
#fields will depend on post type and maybe activity type
#post has to be saved already, b/c Activity is in generic relation to post
activity = Activity(
user = post.get_last_author(),
- active_at = post.added_at,
+ active_at = timestamp,
content_object = post,
activity_type = activity_type
)
@@ -420,8 +411,6 @@ def record_revision_question_event(instance, created, **kwargs):
instance.question.get_author_list(include_comments = True)
)
- receiving_users.update(
- )
for a in instance.question.answers.all():
receiving_users.update(a.get_author_list())
@@ -562,6 +551,8 @@ def record_cancel_vote(instance, **kwargs):
#todo: same problem - cannot access receiving user here
activity.save()
+#todo: weird that there is no record delete answer or comment
+#is this even necessary to keep track of?
def record_delete_question(instance, delete_by, **kwargs):
"""
when user deleted the question
@@ -634,7 +625,7 @@ def record_user_full_updated(instance, **kwargs):
def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs):
aq_list = AnonymousQuestion.objects.filter(session_key = session_key)
aa_list = AnonymousAnswer.objects.filter(session_key = session_key)
- from forum.conf import settings as forum_settings
+ #from forum.conf import settings as forum_settings
if forum_settings.EMAIL_VALIDATION == True:#add user to the record
for aq in aq_list:
aq.author = user
@@ -652,7 +643,6 @@ def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs)
#signal for User model save changes
django_signals.pre_save.connect(calculate_gravatar_hash, sender=User)
django_signals.post_save.connect(record_ask_event, sender=Question)
-django_signals.post_save.connect(record_answer_event, sender=Answer)
django_signals.post_save.connect(record_revision_question_event, sender=QuestionRevision)
django_signals.post_save.connect(record_revision_answer_event, sender=AnswerRevision)
django_signals.post_save.connect(record_award_event, sender=Award)
@@ -675,6 +665,10 @@ signals.post_updated.connect(
record_post_update_activity,
sender=Comment
)
+signals.post_updated.connect(
+ record_post_update_activity,
+ sender=Answer
+ )
#post_syncdb.connect(create_fulltext_indexes)
#todo: wtf??? what is x=x about?
diff --git a/forum/models/answer.py b/forum/models/answer.py
index 3f52b4ab..4b35fd31 100644
--- a/forum/models/answer.py
+++ b/forum/models/answer.py
@@ -1,18 +1,15 @@
-from base import AnonymousContent, ContentRevision, DeletableContent
-from content import Content
-#todo: take care of copy-paste markdowner stuff maybe make html automatic field?
-from forum import const
-from markdown2 import Markdown
-from django.utils.html import strip_tags
-from forum.utils.html import sanitize_html
+import datetime
from django.db import models
from django.utils.http import urlquote as django_urlquote
from django.template.defaultfilters import slugify
from django.core.urlresolvers import reverse
-import datetime
-markdowner = Markdown(html4tags=True)
+from forum.models.base import AnonymousContent, DeletableContent
+from forum.models.base import ContentRevision
+from forum.models.base import save_post, parse_post_text
+from forum.models import content
+from forum.models.question import Question
+from forum import const
-from question import Question
class AnswerManager(models.Manager):
def create_new(self, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False):
@@ -22,7 +19,7 @@ class AnswerManager(models.Manager):
added_at = added_at,
wiki = wiki,
text = text,
- html = sanitize_html(markdowner.convert(text)),
+ #.html field is denormalized by the save() call
)
if answer.wiki:
answer.last_edited_by = answer.author
@@ -84,16 +81,19 @@ class AnswerManager(models.Manager):
# cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id])
# return cursor.fetchall()
-class Answer(Content, DeletableContent):
+class Answer(content.Content, DeletableContent):
question = models.ForeignKey('Question', related_name='answers')
accepted = models.BooleanField(default=False)
accepted_at = models.DateTimeField(null=True, blank=True)
objects = AnswerManager()
- class Meta(Content.Meta):
+ class Meta(content.Content.Meta):
db_table = u'answer'
+ save = save_post
+ parse = parse_post_text
+
def apply_edit(self, edited_at=None, edited_by=None, text=None, comment=None, wiki=False):
if text is None:
@@ -105,7 +105,7 @@ class Answer(Content, DeletableContent):
self.last_edited_at = edited_at
self.last_edited_by = edited_by
- self.html = sanitize_html(markdowner.convert(text))
+ #self.html is denormalized in save()
self.text = text
#todo: bug wiki has no effect here
self.save()
diff --git a/forum/models/base.py b/forum/models/base.py
index 110c0deb..1b3a4c2a 100644
--- a/forum/models/base.py
+++ b/forum/models/base.py
@@ -1,99 +1,143 @@
import datetime
-import hashlib
-from urllib import quote_plus, urlencode
from django.db import models
-from django.utils.http import urlquote as django_urlquote
from django.utils.html import strip_tags
-from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext as _
from django.contrib.sitemaps import ping_google
-import django.dispatch
-from django.conf import settings
+#todo: maybe merge forum.utils.markup and forum.utils.html
from forum.utils import markup
+from forum.utils.html import sanitize_html
from django.utils import html
import logging
+from markdown2 import Markdown
+
+markdowner = Markdown(html4tags=True)
#todo: following methods belong to a future common post class
-def render_post_text_and_get_newly_mentioned_users(post,
- urlize_content = False):
+def parse_post_text(post):
+ """typically post has a field to store raw source text
+ in comment it is called .comment, in Question and Answer it is
+ called .text
+ also there is another field called .html (consistent across models)
+ so the goal of this function is to render raw text into .html
+ and extract any metadata given stored in source (currently
+ this metadata is limited by twitter style @mentions
+ but there may be more in the future
+
+ so really it should be renamed into ..._and_get_meta_data
+ """
text = post.get_text()
- if urlize_content:
+ if post._urlize:
text = html.urlize(text)
- if '@' not in text:
- post.html = text
- return list()
-
- from forum.models.user import Activity
-
- mentioned_by = post.get_last_author()
-
- op = post.get_origin_post()
- anticipated_authors = op.get_author_list( include_comments = True, recursive = True )
-
- extra_name_seeds = markup.extract_mentioned_name_seeds(text)
-
- extra_authors = set()
- for name_seed in extra_name_seeds:
- extra_authors.update(User.objects.filter(username__startswith = name_seed))
+ if post._use_markdown:
+ text = sanitize_html(markdowner.convert(text))
- #it is important to preserve order here so that authors of post get mentioned first
- anticipated_authors += list(extra_authors)
+ #todo, add markdown parser call conditional on
+ #post.use_markdown flag
+ post_html = text
+ mentioned_authors = list()
+ removed_mentions = list()
+ if '@' in text:
+ from forum.models.user import Activity
- mentioned_authors, post.html = markup.mentionize_text(text, anticipated_authors)
+ mentioned_by = post.get_last_author()
- #maybe delete some previous mentions
- if post.id != None:
- #only look for previous mentions if post was already saved before
- prev_mention_qs = Activity.objects.get_mentions(
- mentioned_in = post
+ op = post.get_origin_post()
+ anticipated_authors = op.get_author_list(
+ include_comments = True,
+ recursive = True
)
- new_set = set(mentioned_authors)
- for mention in prev_mention_qs:
- delta_set = set(mention.receiving_users.all()) - new_set
- if not delta_set:
- mention.delete()
- new_set -= delta_set
- mentioned_authors = list(new_set)
+ extra_name_seeds = markup.extract_mentioned_name_seeds(text)
- return mentioned_authors
+ extra_authors = set()
+ for name_seed in extra_name_seeds:
+ extra_authors.update(User.objects.filter(
+ username__startswith = name_seed
+ )
+ )
-def save_content(self, urlize_content = False, **kwargs):
+ #it is important to preserve order here so that authors of post
+ #get mentioned first
+ anticipated_authors += list(extra_authors)
+
+ mentioned_authors, post_html = markup.mentionize_text(
+ text,
+ anticipated_authors
+ )
+
+ #find mentions that were removed and identify any previously
+ #entered mentions so that we can send alerts on only new ones
+ if post.pk is not None:
+ #only look for previous mentions if post was already saved before
+ prev_mention_qs = Activity.objects.get_mentions(
+ mentioned_in = post
+ )
+ new_set = set(mentioned_authors)
+ for prev_mention in prev_mention_qs:
+
+ user = prev_mention.get_mentioned_user()
+ if user in new_set:
+ #don't report mention twice
+ new_set.remove(user)
+ else:
+ removed_mentions.append(prev_mention)
+ mentioned_authors = list(new_set)
+
+ data = {
+ 'html': post_html,
+ 'newly_mentioned_users': mentioned_authors,
+ 'removed_mentions': removed_mentions,
+ }
+ return data
+
+def save_post(post, **kwargs):
"""generic save method to use with posts
"""
- new_mentions = self._render_text_and_get_newly_mentioned_users(
- urlize_content
- )
+ data = post.parse()
- from forum.models.user import Activity
+ post.html = data['html']
+ newly_mentioned_users = data['newly_mentioned_users']
+ removed_mentions = data['removed_mentions']
- #this save must precede saving the mention activity
- super(self.__class__, self).save(**kwargs)
+ #a hack allowing to save denormalized .summary field for questions
+ if hasattr(post, 'summary'):
+ post.summary = strip_tags(post.html)[:120]
+
+ #delete removed mentions
+ for rm in removed_mentions:
+ rm.delete()
- post_author = self.get_last_author()
+ created = post.pk is None
- for u in new_mentions:
+ #this save must precede saving the mention activity
+ #because generic relation needs primary key of the related object
+ super(post.__class__, post).save(**kwargs)
+ last_author = post.get_last_author()
+
+ #create new mentions
+ for u in newly_mentioned_users:
+ from forum.models.user import Activity
Activity.objects.create_new_mention(
mentioned_whom = u,
- mentioned_in = self,
- mentioned_by = post_author
+ mentioned_in = post,
+ mentioned_by = last_author
)
-
#todo: this is handled in signal because models for posts
#are too spread out
from forum.models import signals
signals.post_updated.send(
- post = self,
- newly_mentioned_users = new_mentions,
- sender = self.__class__
+ post = post,
+ newly_mentioned_users = newly_mentioned_users,
+ timestamp = post.get_time_of_last_edit(),
+ created = created,
+ sender = post.__class__
)
try:
diff --git a/forum/models/content.py b/forum/models/content.py
index 95064692..5f144f1e 100644
--- a/forum/models/content.py
+++ b/forum/models/content.py
@@ -1,9 +1,10 @@
+import datetime
+import logging
from django.contrib.auth.models import User
from django.contrib.contenttypes import generic
+from django.contrib.sitemaps import ping_google
from django.db import models
-from meta import Comment, Vote, FlaggedItem
-import datetime
-import logging
+from forum.models.meta import Comment, Vote, FlaggedItem
class Content(models.Model):
"""
@@ -35,6 +36,9 @@ class Content(models.Model):
votes = generic.GenericRelation(Vote)
flagged_items = generic.GenericRelation(FlaggedItem)
+ _use_markdown = True
+ _urlize = False
+
class Meta:
abstract = True
app_label = 'forum'
@@ -80,6 +84,12 @@ class Content(models.Model):
def get_last_author(self):
return self.last_edited_by
+ def get_time_of_last_edit(self):
+ if self.last_edited_at:
+ return self.last_edited_at
+ else:
+ return self.added_at
+
def get_author_list(self, include_comments = False, recursive = False, exclude_list = None):
authors = set()
authors.update([r.author for r in self.revisions.all()])
@@ -108,7 +118,11 @@ class Content(models.Model):
return True
else:
- raise Exception('unexpected User.tag_filter_setting %' % self.tag_filter_setting)
+ raise ValueError(
+ 'unexpected User.tag_filter_setting %s' \
+ % self.tag_filter_setting
+ )
+
def post_get_last_update_info(self):#todo: rename this subroutine
when = self.added_at
who = self.author
diff --git a/forum/models/meta.py b/forum/models/meta.py
index d0530694..5f36d76e 100644
--- a/forum/models/meta.py
+++ b/forum/models/meta.py
@@ -1,9 +1,7 @@
+import datetime
from django.db import models
-from base import MetaContent, UserContent
-from base import render_post_text_and_get_newly_mentioned_users
-from base import save_content
from forum import const
-import datetime
+from forum.models import base
class VoteManager(models.Manager):
def get_up_vote_count_from_user(self, user):
@@ -26,7 +24,7 @@ class VoteManager(models.Manager):
return 0
-class Vote(MetaContent, UserContent):
+class Vote(base.MetaContent, base.UserContent):
VOTE_UP = +1
VOTE_DOWN = -1
VOTE_CHOICES = (
@@ -39,7 +37,7 @@ class Vote(MetaContent, UserContent):
objects = VoteManager()
- class Meta(MetaContent.Meta):
+ class Meta(base.MetaContent.Meta):
unique_together = ('content_type', 'object_id', 'user')
db_table = u'vote'
@@ -65,25 +63,28 @@ class FlaggedItemManager(models.Manager):
else:
return 0
-class FlaggedItem(MetaContent, UserContent):
+class FlaggedItem(base.MetaContent, base.UserContent):
"""A flag on a Question or Answer indicating offensive content."""
flagged_at = models.DateTimeField(default=datetime.datetime.now)
objects = FlaggedItemManager()
- class Meta(MetaContent.Meta):
+ class Meta(base.MetaContent.Meta):
unique_together = ('content_type', 'object_id', 'user')
db_table = u'flagged_item'
def __unicode__(self):
return '[%s] flagged at %s' %(self.user, self.flagged_at)
-class Comment(MetaContent, UserContent):
+class Comment(base.MetaContent, base.UserContent):
comment = models.CharField(max_length = const.COMMENT_HARD_MAX_LENGTH)
added_at = models.DateTimeField(default = datetime.datetime.now)
html = models.CharField(max_length = const.COMMENT_HARD_MAX_LENGTH, default='')
- class Meta(MetaContent.Meta):
+ _urlize = True
+ _use_markdown = False
+
+ class Meta(base.MetaContent.Meta):
ordering = ('-added_at',)
db_table = u'comment'
@@ -97,15 +98,13 @@ class Comment(MetaContent, UserContent):
def set_text(self, text):
self.comment = text
- _render_text_and_get_newly_mentioned_users = \
- render_post_text_and_get_newly_mentioned_users
-
- _save = save_content
+ def parse(self):
+ return base.parse_post_text(self)
def save(self,**kwargs):
- self._save(urlize_content = True)
+ base.save_post(self)
- def get_updated_activity_type(self):
+ def get_updated_activity_type(self, created = False):
if self.content_object.__class__.__name__ == 'Question':
return const.TYPE_ACTIVITY_COMMENT_QUESTION
elif self.content_object.__class__.__name__ == 'Answer':
@@ -123,6 +122,9 @@ class Comment(MetaContent, UserContent):
users -= set([self.user])#remove activity user
return list(users)
+ def get_time_of_last_edit(self):
+ return self.added_at
+
def delete(self, **kwargs):
#todo: not very good import in models of other models
#todo: potentially a circular import
diff --git a/forum/models/question.py b/forum/models/question.py
index 6a36e0aa..0d63426d 100644
--- a/forum/models/question.py
+++ b/forum/models/question.py
@@ -1,23 +1,20 @@
-from base import DeletableContent, AnonymousContent, ContentRevision
-from content import Content
-from forum.models import signals
-from tag import Tag
-from forum import const
-from forum.utils.html import sanitize_html
-from markdown2 import Markdown
-from django.utils.html import strip_tags
+import logging
import datetime
from django.conf import settings
from django.utils.datastructures import SortedDict
-from forum.models.tag import MarkedTag
from django.db import models
from django.contrib.auth.models import User
-from django.utils.http import urlquote as django_urlquote
+from django.utils.http import urlquote as django_urlquote
from django.template.defaultfilters import slugify
from django.core.urlresolvers import reverse
-
-markdowner = Markdown(html4tags=True)
-
+from django.contrib.sitemaps import ping_google
+from django.utils.translation import ugettext as _
+from forum.models.tag import Tag, MarkedTag
+from forum.models import signals
+from forum.models.base import AnonymousContent, DeletableContent, ContentRevision
+from forum.models.base import save_post, parse_post_text
+from forum.models import content
+from forum import const
from forum.utils.lists import LazyList
#todo: too bad keys are duplicated see const sort methods
@@ -35,8 +32,7 @@ QUESTION_ORDER_BY_MAP = {
class QuestionManager(models.Manager):
def create_new(self, title=None,author=None,added_at=None, wiki=False,tagnames=None, text=None):
- html = sanitize_html(markdowner.convert(text))
- summary = strip_tags(html)[:120]
+
question = Question(
title = title,
author = author,
@@ -45,11 +41,15 @@ class QuestionManager(models.Manager):
last_activity_by = author,
wiki = wiki,
tagnames = tagnames,
- html = html,
+ #html field is denormalized in .save() call
text = text,
- summary = summary
+ #summary field is denormalized in .save() call
)
if question.wiki:
+ #todo: this is confusing - last_edited_at field
+ #is used as an indicator whether question has been edited
+ #in template forum/skins/default/templates/post_contributor_info.html
+ #but in principle, post creation should count as edit as well
question.last_edited_by = question.author
question.last_edited_at = added_at
question.wikified_at = added_at
@@ -233,7 +233,7 @@ class QuestionManager(models.Manager):
return False
- #todo: why not make this into a method of class Question?
+ #todo: why not make this into a method of Question class?
# also it is actually strange - why do we need the answer_count
# field if the count depends on who is requesting this?
def update_answer_count(self, question):
@@ -244,7 +244,7 @@ class QuestionManager(models.Manager):
# for some reasons, this Answer class failed to be imported,
# although we have imported all classes from models on top.
- from answer import Answer
+ from forum.models.answer import Answer
self.filter(id=question.id).update(
answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count())
@@ -285,7 +285,7 @@ class QuestionManager(models.Manager):
return LazyList(get_data)
-class Question(Content, DeletableContent):
+class Question(content.Content, DeletableContent):
title = models.CharField(max_length=300)
tags = models.ManyToManyField('Tag', related_name='questions')
answer_accepted = models.BooleanField(default=False)
@@ -312,9 +312,11 @@ class Question(Content, DeletableContent):
objects = QuestionManager()
- class Meta(Content.Meta):
+ class Meta(content.Content.Meta):
db_table = u'question'
+ parse = parse_post_text
+
def delete(self):
super(Question, self).delete()
try:
@@ -335,8 +337,8 @@ class Question(Content, DeletableContent):
# Update the Question's tag associations
signals.tags_updated = self.objects.update_tags(
self,
- form.cleaned_data['tags'],
- request.user
+ tagnames,
+ retagged_by
)
# Create a new revision
@@ -351,7 +353,7 @@ class Question(Content, DeletableContent):
text = latest_revision.text
)
# send tags updated singal
- signals.tags_updated.send(sender=question.__class__, question=self)
+ signals.tags_updated.send(sender=Question, question=self)
def get_origin_post(self):
return self
@@ -374,10 +376,6 @@ class Question(Content, DeletableContent):
if edited_at is None:
edited_at = datetime.datetime.now()
- #todo: have this copy-paste in few places
- html = sanitize_html(markdowner.convert(text))
- question_summary = strip_tags(html)[:120]
-
# Update the Question itself
self.title = title
self.last_edited_at = edited_at
@@ -385,8 +383,6 @@ class Question(Content, DeletableContent):
self.last_edited_by = edited_by
self.last_activity_by = edited_by
self.tagnames = tags
- self.summary = question_summary
- self.html = html
self.text = text
#wiki is an eternal trap whence there is no exit
@@ -436,13 +432,15 @@ class Question(Content, DeletableContent):
This is required as we're using ``tagnames`` as the sole means of
adding and editing tags.
"""
- initial_addition = (self.id is None)
-
- super(Question, self).save(**kwargs)
+ initial_addition = (self.pk is None)
+
+ save_post(self, **kwargs)
if initial_addition:
- tags = Tag.objects.get_or_create_multiple(self.tagname_list(),
- self.author)
+ tags = Tag.objects.get_or_create_multiple(
+ self.tagname_list(),
+ self.author
+ )
self.tags.add(*tags)
Tag.objects.update_use_counts(tags)
@@ -454,7 +452,10 @@ class Question(Content, DeletableContent):
return u','.join([unicode(tag) for tag in self.tagname_list()])
def get_absolute_url(self):
- return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title)))
+ return '%s%s' % (
+ reverse('question', args=[self.id]),
+ django_urlquote(slugify(self.title))
+ )
def has_favorite_by_user(self, user):
if not user.is_authenticated():
@@ -463,7 +464,7 @@ class Question(Content, DeletableContent):
return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0
def get_answer_count_by_user(self, user_id):
- from answer import Answer
+ from forum.models.answer import Answer
query_set = Answer.objects.filter(author__id=user_id)
return query_set.filter(question=self).count()
@@ -523,6 +524,7 @@ class Question(Content, DeletableContent):
answer_comments.append(comment)
#create the report
+ from forum.conf import settings as forum_settings
if edited or new_answers or modified_answers or answer_comments:
out = []
if edited:
@@ -618,5 +620,3 @@ class AnonymousQuestion(AnonymousContent):
text=self.text,
)
self.delete()
-
-from answer import Answer, AnswerManager
diff --git a/forum/models/tag.py b/forum/models/tag.py
index 0e585547..89f706f5 100644
--- a/forum/models/tag.py
+++ b/forum/models/tag.py
@@ -1,9 +1,9 @@
-from base import DeletableContent
from django.db import models
from django.db import connection, transaction
from django.contrib.auth.models import User
-
from django.utils.translation import ugettext as _
+from forum.models.base import DeletableContent
+
class TagManager(models.Manager):
UPDATE_USED_COUNTS_QUERY = (
diff --git a/forum/models/user.py b/forum/models/user.py
index c05d911b..083aca79 100644
--- a/forum/models/user.py
+++ b/forum/models/user.py
@@ -1,18 +1,17 @@
-#todo: remove this with Django 1.2
+from hashlib import md5
+import string
+from random import Random
+import datetime
+import logging
from django.db import models
+from django.db.backends.dummy.base import IntegrityError
from django.contrib.contenttypes.models import ContentType
-from forum.models import signals
from django.contrib.contenttypes import generic
from django.contrib.auth.models import User
-from hashlib import md5
-import string
-from random import Random
+from django.utils.translation import ugettext as _
from forum import const
from forum.utils import functions
-import datetime
-import logging
-from django.utils.translation import ugettext as _
class ActivityManager(models.Manager):
def get_all_origin_posts(self):
@@ -80,8 +79,12 @@ class ActivityManager(models.Manager):
mentioned_whom = None,
mentioned_at = None,
mentioned_in = None,
- reported = None
+ reported = None,
+ mentioned_at__gt = None,
):
+ """extract mention-type activity objects
+ todo: implement better rich field lookups
+ """
kwargs = dict()
@@ -90,6 +93,8 @@ class ActivityManager(models.Manager):
if mentioned_at:
#todo: handle cases with rich lookups here like __lt
kwargs['active_at'] = mentioned_at
+ elif mentioned_at__gt:
+ kwargs['active_at__gt'] = mentioned_at__gt
if mentioned_by:
kwargs['user'] = mentioned_by
@@ -139,6 +144,12 @@ class Activity(models.Model):
app_label = 'forum'
db_table = u'activity'
+ def get_mentioned_user(self):
+ assert(self.activity_type == const.TYPE_ACTIVITY_MENTION)
+ user_qs = self.receiving_users.all()
+ assert(len(user_qs) == 1)
+ return user_qs[0]
+
class EmailFeedSettingManager(models.Manager):
def exists_match_to_post_and_subscriber(