From 410bfa05ee36ed1d99356c443a5f3f6aa3ee9578 Mon Sep 17 00:00:00 2001 From: hernani Date: Fri, 7 May 2010 01:15:39 +0000 Subject: [PATCH] Merging the experimental branch back to trunk. git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@156 0cfe37f9-358a-4d5e-be75-b63607b5c754 --- forum/actions/__init__.py | 3 + forum/actions/meta.py | 207 ++++++ forum/actions/node.py | 115 +++ forum/actions/user.py | 53 ++ forum/activity.py | 121 ---- forum/admin.py | 14 +- forum/akismet.py | 4 +- forum/authentication/__init__.py | 1 - forum/badges/__init__.py | 11 +- forum/badges/base.py | 133 ++-- forum/feed.py | 2 +- forum/forms.py | 12 - forum/management/__init__.py | 10 - ...uto__add_field_question_accepted_answer.py | 7 +- ...ion__add_favoritenode__del_field_node_v.py | 463 ++++++++++++ ..._flaten_node_inheritance_create_actions.py | 685 ++++++++++++++++++ ..._answer__del_flaggeditem__del_anonymous.py | 377 ++++++++++ ...on__add_field_node_deleted__add_field_n.py | 267 +++++++ ...0026_reset_deleted_and_lastedited_flags.py | 278 +++++++ forum/migrations/0027_auto__del_activity.py | 254 +++++++ ...anceled_ip__add_field_actionrepute_date.py | 253 +++++++ forum/migrations/0029_repute_dates.py | 259 +++++++ ..._action_extra__chg_field_keyvalue_value.py | 253 +++++++ forum/migrations/0031_alter_pickle_storage.py | 274 +++++++ ...ed_questions__del_field_user_questions_.py | 263 +++++++ ...ield_badge_cls__del_unique_badge_type_n.py | 333 +++++++++ forum/migrations/0034_new_badge_and_award.py | 344 +++++++++ ...__del_field_award_content_type__add_uni.py | 296 ++++++++ forum/models/__init__.py | 30 +- forum/models/action.py | 268 +++++++ forum/models/answer.py | 72 +- forum/models/base.py | 127 ++-- forum/models/comment.py | 21 +- forum/models/meta.py | 161 ++-- forum/models/node.py | 164 +++-- forum/models/question.py | 105 +-- forum/models/repute.py | 127 ---- forum/models/tag.py | 1 - forum/models/user.py | 164 ++--- forum/models/utils.py | 80 +- forum/{modules.py => modules/__init__.py} | 2 +- forum/modules/decorators.py | 53 ++ forum/reputation.py | 101 --- forum/settings/__init__.py | 1 + forum/settings/base.py | 126 +--- forum/settings/basic.py | 2 +- forum/settings/email.py | 2 +- forum/settings/extkeys.py | 3 +- forum/settings/form.py | 12 +- forum/settings/forms.py | 44 +- forum/settings/moderation.py | 24 + .../skins/default/media/images/openid/aol.gif | Bin 2205 -> 2116 bytes .../default/media/images/openid/blogger.png | Bin 0 -> 194 bytes .../default/media/images/openid/claimid.png | Bin 0 -> 453 bytes .../default/media/images/openid/facebook.gif | Bin 2075 -> 2035 bytes .../default/media/images/openid/flickr.png | Bin 0 -> 270 bytes .../default/media/images/openid/google.gif | Bin 1596 -> 1888 bytes .../media/images/openid/livejournal.png | Bin 0 -> 471 bytes .../default/media/images/openid/myopenid.png | Bin 0 -> 686 bytes .../media/images/openid/technorati.png | Bin 0 -> 422 bytes .../default/media/images/openid/twitter.png | Bin 3130 -> 2198 bytes .../default/media/images/openid/verisign.png | Bin 0 -> 513 bytes .../default/media/images/openid/wordpress.png | Bin 0 -> 268 bytes .../default/media/images/openid/yahoo.gif | Bin 1510 -> 1643 bytes forum/skins/default/media/js/jquery.openid.js | 7 +- forum/skins/default/media/js/osqa.admin.js | 21 + forum/skins/default/media/js/osqa.main.js | 256 +++++-- forum/skins/default/media/style/admin.css | 19 +- forum/skins/default/media/style/auth.css | 52 +- forum/skins/default/media/style/style.css | 134 +++- forum/skins/default/templates/ask.html | 2 +- .../skins/default/templates/auth/signin.html | 63 +- forum/skins/default/templates/badge.html | 3 +- forum/skins/default/templates/faq.html | 2 +- .../default/templates/node/comments.html | 100 ++- .../default/templates/node/post_controls.html | 2 +- .../skins/default/templates/node/report.html | 19 + .../default/templates/osqaadmin/base.html | 16 +- .../default/templates/osqaadmin/index.html | 2 +- .../templates/post_contributor_info.html | 12 +- forum/skins/default/templates/question.html | 20 +- .../templates/question_list/tag_selector.html | 2 + .../default/templates/question_retag.html | 2 +- .../templates/question_summary_list_roll.html | 2 +- forum/skins/default/templates/reopen.html | 2 +- .../templates/sidebar/recent_awards.html | 6 +- .../default/templates/users/activity.html | 18 +- forum/skins/default/templates/users/edit.html | 8 +- forum/skins/default/templates/users/info.html | 13 +- .../default/templates/users/moderation.html | 4 + .../default/templates/users/questions.html | 4 +- .../skins/default/templates/users/recent.html | 2 +- .../default/templates/users/reputation.html | 2 +- .../skins/default/templates/users/stats.html | 4 +- .../skins/default/templates/users/votes.html | 2 +- forum/startup.py | 2 - forum/subscriptions.py | 170 ++--- forum/templatetags/extra_tags.py | 3 + forum/templatetags/general_sidebar_tags.py | 2 +- forum/templatetags/node_tags.py | 56 +- forum/templatetags/user_tags.py | 47 +- forum/urls.py | 12 +- forum/views/admin.py | 15 +- forum/views/auth.py | 8 +- forum/views/commands.py | 258 +++---- forum/views/decorators.py | 33 +- forum/views/meta.py | 41 +- forum/views/readers.py | 42 +- forum/views/users.py | 71 +- forum/views/writers.py | 81 +-- forum_modules/default_badges/badges.py | 456 ++++++------ .../localauth/templates/loginform.html | 3 +- forum_modules/localauth/views.py | 2 + forum_modules/openidauth/authentication.py | 33 +- .../openidauth/templates/openidurl.html | 2 +- forum_modules/pgfulltext/handlers.py | 14 +- forum_modules/pgfulltext/pg_fts_install.sql | 4 +- forum_modules/pgfulltext/settings.py | 3 + forum_modules/pgfulltext/startup.py | 25 +- forum_modules/project_badges/badges.py | 27 +- mdx_limitedsyntax.py | 11 + settings.py | 2 +- settings_local.py.dist | 2 +- 123 files changed, 7123 insertions(+), 2085 deletions(-) create mode 100644 forum/actions/__init__.py create mode 100644 forum/actions/meta.py create mode 100644 forum/actions/node.py create mode 100644 forum/actions/user.py delete mode 100644 forum/activity.py create mode 100644 forum/migrations/0022_auto__add_actionrepute__add_action__add_favoritenode__del_field_node_v.py create mode 100644 forum/migrations/0023_flaten_node_inheritance_create_actions.py create mode 100644 forum/migrations/0024_auto__del_repute__del_vote__del_answer__del_flaggeditem__del_anonymous.py create mode 100644 forum/migrations/0025_auto__add_field_node_extra_action__add_field_node_deleted__add_field_n.py create mode 100644 forum/migrations/0026_reset_deleted_and_lastedited_flags.py create mode 100644 forum/migrations/0027_auto__del_activity.py create mode 100644 forum/migrations/0028_auto__add_field_action_canceled_ip__add_field_actionrepute_date.py create mode 100644 forum/migrations/0029_repute_dates.py create mode 100644 forum/migrations/0030_auto__chg_field_action_extra__chg_field_keyvalue_value.py create mode 100644 forum/migrations/0031_alter_pickle_storage.py create mode 100644 forum/migrations/0032_auto__del_field_user_hide_ignored_questions__del_field_user_questions_.py create mode 100644 forum/migrations/0033_auto__add_flag__add_vote__add_field_badge_cls__del_unique_badge_type_n.py create mode 100644 forum/migrations/0034_new_badge_and_award.py create mode 100644 forum/migrations/0035_auto__del_field_award_object_id__del_field_award_content_type__add_uni.py create mode 100644 forum/models/action.py delete mode 100644 forum/models/repute.py rename forum/{modules.py => modules/__init__.py} (97%) create mode 100644 forum/modules/decorators.py delete mode 100644 forum/reputation.py create mode 100644 forum/settings/moderation.py create mode 100644 forum/skins/default/media/images/openid/blogger.png create mode 100644 forum/skins/default/media/images/openid/claimid.png create mode 100644 forum/skins/default/media/images/openid/flickr.png create mode 100644 forum/skins/default/media/images/openid/livejournal.png create mode 100644 forum/skins/default/media/images/openid/myopenid.png create mode 100644 forum/skins/default/media/images/openid/technorati.png create mode 100644 forum/skins/default/media/images/openid/verisign.png create mode 100644 forum/skins/default/media/images/openid/wordpress.png create mode 100644 forum/skins/default/media/js/osqa.admin.js create mode 100644 forum/skins/default/templates/node/report.html create mode 100644 forum/skins/default/templates/users/moderation.html create mode 100644 forum_modules/pgfulltext/settings.py create mode 100644 mdx_limitedsyntax.py diff --git a/forum/actions/__init__.py b/forum/actions/__init__.py new file mode 100644 index 0000000..a681069 --- /dev/null +++ b/forum/actions/__init__.py @@ -0,0 +1,3 @@ +from meta import * +from node import * +from user import * \ No newline at end of file diff --git a/forum/actions/meta.py b/forum/actions/meta.py new file mode 100644 index 0000000..a45fe58 --- /dev/null +++ b/forum/actions/meta.py @@ -0,0 +1,207 @@ +from django.utils.translation import ugettext as _ +from django.db.models import F +from forum.models.action import ActionProxy, DummyActionProxy +from forum.models import Vote, Flag +import settings + +class VoteAction(ActionProxy): + def update_node_score(self, inc): + self.node.score = F('score') + inc + self.node.save() + + def process_vote_action(self, value): + self.update_node_score(value) + vote = Vote(node=self.node, user=self.user, action=self, value=value) + vote.save() + + def cancel_action(self): + vote = self.vote.all()[0] + self.update_node_score(-vote.value) + vote.delete() + + @classmethod + def get_for(cls, user, node): + try: + vote = Vote.objects.get(user=user, node=node) + return vote.value + except: + return None + + def describe_vote(self, vote_desc, viewer=None): + return _("%(user)s %(vote_desc)s %(post_desc)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'vote_desc': vote_desc, 'post_desc': self.describe_node(viewer, self.node) + } + + +class VoteUpAction(VoteAction): + def repute_users(self): + self.repute(self.node.author, int(settings.REP_GAIN_BY_UPVOTED)) + + def process_action(self): + self.process_vote_action(1) + self.user.reset_vote_up_count_cache() + + def cancel_action(self): + super(VoteUpAction, self).cancel_action() + self.user.reset_vote_up_count_cache() + + def describe(self, viewer=None): + return self.describe_vote(_("voted up"), viewer) + +class VoteDownAction(VoteAction): + def repute_users(self): + self.repute(self.node.author, -int(settings.REP_LOST_BY_DOWNVOTED)) + self.repute(self.user, -int(settings.REP_LOST_BY_DOWNVOTING)) + + def process_action(self): + self.process_vote_action(-1) + self.user.reset_vote_down_count_cache() + + def cancel_action(self): + super(VoteDownAction, self).cancel_action() + self.user.reset_vote_down_count_cache() + + def describe(self, viewer=None): + return self.describe_vote(_("voted down"), viewer) + + +class VoteUpCommentAction(VoteUpAction): + def repute_users(self): + pass + + def process_action(self): + self.process_vote_action(1) + + def cancel_action(self): + super(VoteUpAction, self).cancel_action() + + def describe(self, viewer=None): + return self.describe_vote(_("liked"), viewer) + + +class FlagAction(ActionProxy): + def repute_users(self): + self.repute(self.node.author, -int(settings.REP_LOST_BY_FLAGGED)) + + def process_action(self): + flag = Flag(user=self.user, node=self.node, action=self, reason=self.extra) + flag.save() + self.node.reset_flag_count_cache() + + if self.node.flag_count == int(settings.FLAG_COUNT_TO_HIDE_POST): + self.repute(self.node.author, -int(settings.REP_LOST_BY_FLAGGED_3_TIMES)) + + if self.node.flag_count == int(settings.FLAG_COUNT_TO_DELETE_POST): + self.repute(self.node.author, -int(settings.REP_LOST_BY_FLAGGED_5_TIMES)) + if not self.node.deleted: + DeleteAction(node=self.node, user=self.user, extra="BYFLAGGED").save() + + def cancel_action(self): + self.flag.all()[0].delete() + self.node.reset_flag_count_cache() + + @classmethod + def get_for(cls, user, node): + try: + flag = Flag.objects.get(user=user, node=node) + return flag.reason or _("No reason given") + except: + return None + + def describe(self, viewer=None): + return _("%(user)s flagged %(post_desc)s: %(reason)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'post_desc': self.describe_node(viewer, self.node), 'reason': self.extra + } + + +class AcceptAnswerAction(ActionProxy): + def repute_users(self): + if (self.user == self.node.parent.author) and (not self.user == self.node.author): + self.repute(self.user, int(settings.REP_GAIN_BY_ACCEPTING)) + + if self.user != self.node.author: + self.repute(self.node.author, int(settings.REP_GAIN_BY_ACCEPTED)) + + def process_action(self): + self.node.parent.extra_ref = self.node + self.node.parent.save() + self.node.marked = True + self.node.extra_action = self + self.node.save() + + def cancel_action(self): + self.node.parent.extra_ref = None + self.node.parent.save() + self.node.marked = False + self.node.extra_action = None + self.node.save() + + def describe(self, viewer=None): + answer = self.node + question = answer.parent + + if self.user == question.author: + asker = (self.user == viewer) and _("your") or _("his") + else: + asker = self.hyperlink(question.author.get_profile_url(), question.author.username) + + return _("%(user)s accepted %(answerer)s answer on %(asker)s question %(question)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'answerer': self.hyperlink(answer.author.get_profile_url(), self.friendly_username(viewer, answer.author)), + 'asker': asker, + 'question': self.hyperlink(question.get_absolute_url(), question.title) + } + + +class FavoriteAction(ActionProxy): + def process_action(self): + self.node.reset_favorite_count_cache() + + def cancel_action(self): + self.process_action() + + def describe(self, viewer=None): + return _("%(user)s marked %(post_desc)s as favorite") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'post_desc': self.describe_node(viewer, self.node), + } + + +class DeleteAction(ActionProxy): + def process_action(self): + self.node.deleted = self + self.node.save() + + if self.node.node_type == "answer": + self.node.question.reset_answer_count_cache() + + def cancel_action(self): + self.node.deleted = None + self.node.save() + + if self.node.node_type == "answer": + self.node.question.reset_answer_count_cache() + + def describe(self, viewer=None): + return _("%(user)s deleted %(post_desc)s: %(reason)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'post_desc': self.describe_node(viewer, self.node), 'reason': self.reason(), + } + + def reason(self): + if self.extra != "BYFLAGGED": + return self.extra + else: + return _("flagged by multiple users: ") + "; ".join([f.extra for f in FlagAction.objects.filter(node=self.node)]) + + +class QuestionViewAction(DummyActionProxy): + def __init__(self, question, user): + self.viewuser = user + self.question = question + + def process_action(self): + self.question.extra_count = F('extra_count') + 1 + self.question.save() diff --git a/forum/actions/node.py b/forum/actions/node.py new file mode 100644 index 0000000..d7f9383 --- /dev/null +++ b/forum/actions/node.py @@ -0,0 +1,115 @@ +from django.utils.html import strip_tags +from django.utils.translation import ugettext as _ +from forum.models.action import ActionProxy +from forum.models import Comment, Question, Answer, NodeRevision + +class NodeEditAction(ActionProxy): + def create_revision_data(self, initial=False, **data): + revision_data = dict(summary=data.get('summary', (initial and _('Initial revision' or ''))), body=data['text']) + + if data.get('title', None): + revision_data['title'] = strip_tags(data['title'].strip()) + + if data.get('tags', None): + revision_data['tagnames'] = data['tags'].strip() + + return revision_data + +class AskAction(NodeEditAction): + def process_data(self, **data): + question = Question(author=self.user, **self.create_revision_data(True, **data)) + question.save() + self.node = question + + def describe(self, viewer=None): + return _("%(user)s asked %(question)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'question': self.hyperlink(self.node.get_absolute_url(), self.node.title) + } + +class AnswerAction(NodeEditAction): + def process_data(self, **data): + answer = Answer(author=self.user, parent=data['question'], **self.create_revision_data(True, **data)) + answer.save() + self.node = answer + + def process_action(self): + self.node.question.reset_answer_count_cache() + + def describe(self, viewer=None): + question = self.node.parent + return _("%(user)s answered %(asker)s %(question)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'asker': self.hyperlink(question.author.get_profile_url(), self.friendly_username(viewer, question.author)), + 'question': self.hyperlink(self.node.get_absolute_url(), question.title) + } + +class CommentAction(ActionProxy): + def process_data(self, text='', parent=None): + comment = Comment(author=self.user, parent=parent, body=text) + comment.save() + self.node = comment + + def describe(self, viewer=None): + return _("%(user)s commented on %(post_desc)s") % { + 'user': self.hyperlink(self.node.author.get_profile_url(), self.friendly_username(viewer, self.node.author)), + 'post_desc': self.describe_node(viewer, self.node.parent) + } + +class ReviseAction(NodeEditAction): + def process_data(self, **data): + revision_data = self.create_revision_data(**data) + revision = self.node.create_revision(self.user, action=self, **revision_data) + self.extra = revision.revision + + def describe(self, viewer=None): + return _("%(user)s edited %(post_desc)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'post_desc': self.describe_node(viewer, self.node) + } + +class RetagAction(ActionProxy): + def process_data(self, tagnames=''): + active = self.node.active_revision + revision_data = dict(summary=_('Retag'), title=active.title, tagnames=strip_tags(tagnames.strip()), body=active.body) + self.node.create_revision(self.user, action=self, **revision_data) + + def describe(self, viewer=None): + return _("%(user)s retagged %(post_desc)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'post_desc': self.describe_node(viewer, self.node) + } + +class RollbackAction(ActionProxy): + def process_data(self, activate=None): + previous = self.node.active_revision + self.node.activate_revision(self.user, activate, self) + self.extra = "%d:%d" % (previous.revision, activate.revision) + + def describe(self, viewer=None): + revisions = [NodeRevision.objects.get(node=self.node, revision=int(n)) for n in self.extra.split(':')] + + return _("%(user)s reverted %(post_desc)s from revision %(initial)d (%(initial_sum)s) to revision %(final)d (%(final_sum)s)") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'post_desc': self.describe_node(viewer, self.node), + 'initial': revisions[0].revision, 'initial_sum': revisions[0].summary, + 'final': revisions[1].revision, 'final_sum': revisions[1].summary, + } + +class CloseAction(ActionProxy): + def process_action(self): + self.node.extra_action = self + self.node.marked = True + self.node.save() + + def cancel_action(self): + self.node.extra_action = None + self.node.marked = False + self.node.save() + + def describe(self, viewer=None): + return _("%(user)s closed %(post_desc)s: %(reason)s") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'post_desc': self.describe_node(viewer, self.node), + 'reason': self.extra + } \ No newline at end of file diff --git a/forum/actions/user.py b/forum/actions/user.py new file mode 100644 index 0000000..2695f9f --- /dev/null +++ b/forum/actions/user.py @@ -0,0 +1,53 @@ +from django.utils.translation import ugettext as _ +from django.db.models import F +from forum.models.action import ActionProxy +from forum.models import Award +from forum import settings + +class UserJoinsAction(ActionProxy): + def repute_users(self): + self.repute(self.user, int(settings.INITIAL_REP)) + +class EditProfileAction(ActionProxy): + pass + +class AwardAction(ActionProxy): + def process_data(self, badge, trigger): + self.__dict__['_badge'] = badge + self.__dict__['_trigger'] = trigger + + def process_action(self): + badge = self.__dict__['_badge'] + trigger = self.__dict__['_trigger'] + + award = Award(user=self.user, badge=badge, trigger=trigger, action=self) + if self.node: + award.node = self.node + + award.save() + award.badge.awarded_count = F('awarded_count') + 1 + award.badge.save() + self.user.message_set.create(message=_("Congratulations, you have received a badge '%(badge_name)s'. " \ + + u"Check out your profile.") % dict(badge_name=award.badge.name, + profile_url=self.user.get_profile_url())) + + def cancel_action(self): + award = self.award + badge = award.badge + badge.awarded_count = F('awarded_count') - 1 + badge.save() + award.delete() + + @classmethod + def get_for(cls, user, node, badge): + try: + award = Award.objects.get(user=user, node=node, badge=badge) + return award.action + except: + return None + + def describe(self, viewer=None): + return _("%(user)s was awarded the %(badge_name)s badge") % { + 'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)), + 'badge_name': self.award.all()[0].badge.name, + } \ No newline at end of file diff --git a/forum/activity.py b/forum/activity.py deleted file mode 100644 index 1687798..0000000 --- a/forum/activity.py +++ /dev/null @@ -1,121 +0,0 @@ -import datetime -from django.db.models.signals import post_save -from forum.models import * -from forum.models.base import marked_deleted, mark_canceled -from forum.models.node import node_create -from forum.models.answer import answer_accepted -from forum.authentication import user_updated -from forum.const import * - -def record_ask_event(instance, **kwargs): - activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ASK_QUESTION) - activity.save() - -node_create.connect(record_ask_event, sender=Question) - - -def record_answer_event(instance, **kwargs): - activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ANSWER) - activity.save() - -node_create.connect(record_answer_event, sender=Answer) - - -def record_comment_event(instance, **kwargs): - act_type = (instance.content_object.__class__ is Question) and TYPE_ACTIVITY_COMMENT_QUESTION or TYPE_ACTIVITY_COMMENT_ANSWER - activity = Activity(user=instance.user, active_at=instance.added_at, content_object=instance, activity_type=act_type) - activity.save() - -node_create.connect(record_comment_event, sender=Comment) - - -def record_revision_event(instance, created, **kwargs): - if created and instance.revision <> 1 and instance.node.node_type in ('question', 'answer',): - activity_type = instance.node.node_type == 'question' and TYPE_ACTIVITY_UPDATE_QUESTION or TYPE_ACTIVITY_UPDATE_ANSWER - activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=activity_type) - activity.save() - -post_save.connect(record_revision_event, sender=NodeRevision) - - -def record_award_event(instance, created, **kwargs): - if created: - activity = Activity(user=instance.user, active_at=instance.awarded_at, content_object=instance, - activity_type=TYPE_ACTIVITY_PRIZE) - activity.save() - -post_save.connect(record_award_event, sender=Award) - - -def record_answer_accepted(answer, user, **kwargs): - activity = Activity(user=user, active_at=datetime.datetime.now(), content_object=answer, activity_type=TYPE_ACTIVITY_MARK_ANSWER) - activity.save() - -answer_accepted.connect(record_answer_accepted) - - -def update_last_seen(instance, **kwargs): - user = instance.user - user.last_seen = datetime.datetime.now() - user.save() - -post_save.connect(update_last_seen, sender=Activity) - - -def record_vote(instance, created, **kwargs): - if created: - act_type = (instance.vote == 1) and TYPE_ACTIVITY_VOTE_UP or TYPE_ACTIVITY_VOTE_DOWN - - activity = Activity(user=instance.user, active_at=instance.voted_at, content_object=instance, activity_type=act_type) - activity.save() - -post_save.connect(record_vote, sender=Vote) - - -def record_cancel_vote(instance, **kwargs): - act_type = (instance.vote == 1) and TYPE_ACTIVITY_CANCEL_VOTE_UP or TYPE_ACTIVITY_CANCEL_VOTE_DOWN - activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=act_type) - activity.save() - -mark_canceled.connect(record_cancel_vote, sender=Vote) - - -def record_delete_post(instance, **kwargs): - act_type = (instance.__class__ is Question) and TYPE_ACTIVITY_DELETE_QUESTION or TYPE_ACTIVITY_DELETE_ANSWER - activity = Activity(user=instance.deleted_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=act_type) - activity.save() - -marked_deleted.connect(record_delete_post, sender=Question) -marked_deleted.connect(record_delete_post, sender=Answer) - - -def record_update_tags(instance, created, **kwargs): - if not created and 'tagnames' in instance.get_dirty_fields(): - activity = Activity(user=instance.author, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_TAGS) - activity.save() - -post_save.connect(record_update_tags, sender=Question) - - -def record_mark_offensive(instance, created, **kwargs): - if created: - activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance.content_object, activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE) - activity.save() - -post_save.connect(record_mark_offensive, sender=FlaggedItem) - - -def record_favorite_question(instance, created, **kwargs): - if created: - activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_FAVORITE) - activity.save() - -post_save.connect(record_favorite_question, sender=FavoriteQuestion) - - -def record_user_full_updated(instance, **kwargs): - activity = Activity(user=instance, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED) - activity.save() - -user_updated.connect(record_user_full_updated, sender=User) - diff --git a/forum/admin.py b/forum/admin.py index 3afa224..e770806 100644 --- a/forum/admin.py +++ b/forum/admin.py @@ -6,7 +6,7 @@ from models import * class AnonymousQuestionAdmin(admin.ModelAdmin): """AnonymousQuestion admin class""" -class QuestionAdmin(admin.ModelAdmin): +class NodeAdmin(admin.ModelAdmin): """Question admin class""" class TagAdmin(admin.ModelAdmin): @@ -42,7 +42,7 @@ class BadgeAdmin(admin.ModelAdmin): class ReputeAdmin(admin.ModelAdmin): """ admin class""" -class ActivityAdmin(admin.ModelAdmin): +class ActionAdmin(admin.ModelAdmin): """ admin class""" #class BookAdmin(admin.ModelAdmin): @@ -54,19 +54,13 @@ class ActivityAdmin(admin.ModelAdmin): #class BookAuthorRssAdmin(admin.ModelAdmin): # """ admin class""" -admin.site.register(Question, QuestionAdmin) +admin.site.register(Node, NodeAdmin) admin.site.register(Tag, TagAdmin) -admin.site.register(Answer, Answerdmin) -admin.site.register(Comment, CommentAdmin) -admin.site.register(Vote, VoteAdmin) -admin.site.register(FlaggedItem, FlaggedItemAdmin) -admin.site.register(FavoriteQuestion, FavoriteQuestionAdmin) admin.site.register(QuestionRevision, QuestionRevisionAdmin) admin.site.register(AnswerRevision, AnswerRevisionAdmin) admin.site.register(Badge, BadgeAdmin) admin.site.register(Award, AwardAdmin) -admin.site.register(Repute, ReputeAdmin) -admin.site.register(Activity, ActivityAdmin) +admin.site.register(Action, ActionAdmin) #admin.site.register(Book, BookAdmin) #admin.site.register(BookAuthorInfo, BookAuthorInfoAdmin) #admin.site.register(BookAuthorRss, BookAuthorRssAdmin) diff --git a/forum/akismet.py b/forum/akismet.py index 940e5dd..4138d5a 100644 --- a/forum/akismet.py +++ b/forum/akismet.py @@ -119,8 +119,8 @@ class Akismet(object): if agent is None: agent = DEFAULTAGENT % __version__ self.user_agent = user_agent % (agent, __version__) - self.key = settings.WORDPRESS_API_KEY - self.blog_url = settings.WORDPRESS_BLOG_URL + self.key = str(settings.WORDPRESS_API_KEY) + self.blog_url = str(settings.WORDPRESS_BLOG_URL) def _getURL(self): diff --git a/forum/authentication/__init__.py b/forum/authentication/__init__.py index f6e14b7..08ea03f 100644 --- a/forum/authentication/__init__.py +++ b/forum/authentication/__init__.py @@ -34,7 +34,6 @@ AUTH_PROVIDERS = dict([ #todo: probably this don't belong here, also this post_stored routine needs a lot of work user_logged_in = django.dispatch.Signal(providing_args=["user", "old_session"]) -user_updated = django.dispatch.Signal(providing_args=["instance", "updated_by"]) #def post_stored_anonymous_content(user,old_session,**kwargs): # from forum.models import AnonymousQuestion, AnonymousAnswer diff --git a/forum/badges/__init__.py b/forum/badges/__init__.py index 421348f..935effd 100644 --- a/forum/badges/__init__.py +++ b/forum/badges/__init__.py @@ -1,10 +1,3 @@ -import re +from forum.modules import get_modules_script -from forum.badges.base import AbstractBadge -from forum.modules import get_modules_script_classes - -ALL_BADGES = [ - cls() for name, cls - in get_modules_script_classes('badges', AbstractBadge).items() - if not re.search('AbstractBadge$', name) - ] +get_modules_script('badges') diff --git a/forum/badges/base.py b/forum/badges/base.py index 63b6b0b..fce6b6e 100644 --- a/forum/badges/base.py +++ b/forum/badges/base.py @@ -4,111 +4,66 @@ from string import lower from django.contrib.contenttypes.models import ContentType from django.db.models.signals import post_save -from forum.models.user import activity_record -from forum.models.base import denorm_update -from forum.models import Badge, Award, Activity, Node +from forum.models import Badge, Node +from forum.actions import AwardAction import logging -class AbstractBadge(object): - - _instance = None - - @property - def name(self): - return " ".join(re.findall(r'([A-Z][a-z1-9]+)', re.sub('Badge', '', self.__class__.__name__))) - - @property - def description(self): - raise NotImplementedError - - def __new__(cls, *args, **kwargs): - if cls._instance is None: - cls.badge = "-".join(map(lower, re.findall(r'([A-Z][a-z1-9]+)', re.sub('Badge', '', cls.__name__)))) - cls._instance = super(AbstractBadge, cls).__new__(cls, *args, **kwargs) - - return cls._instance - - def install(self): - try: - installed = Badge.objects.get(slug=self.badge) - except: - badge = Badge(name=self.name, description=self.description, slug=self.badge, type=self.type) - badge.save() - - def award_badge(self, user, obj=None, award_once=False): - try: - badge = Badge.objects.get(slug=self.badge) - except: - logging.log(1, 'Trying to award a badge not installed in the database.') - return - - content_type = ContentType.objects.get_for_model(obj.__class__) - - awarded = user.awards.filter(badge=badge) +installed = dict([(b.cls, b) for b in Badge.objects.all()]) - if not award_once: - awarded = awarded.filter(content_type=content_type, object_id=obj.id) +class BadgesMeta(type): + by_class = {} + by_id = {} - if len(awarded): - logging.log(1, 'Trying to award badged already awarded.') - return - - award = Award(user=user, badge=badge, content_type=content_type, object_id=obj.id) - award.save() + def __new__(mcs, name, bases, dic): + badge = type.__new__(mcs, name, bases, dic) -class CountableAbstractBadge(AbstractBadge): + if not dic.get('abstract', False): + if not name in installed: + badge.ondb = Badge(cls=name, type=dic.get('type', Badge.BRONZE)) + badge.ondb.save() + else: + badge.ondb = installed[name] - def __init__(self, model, field, expected_value, handler): - def wrapper(instance, sfield, old, new, **kwargs): - if sfield == field and (new == expected_value) or (old < expected_value and new > expected_value): - handler(instance=instance) - - denorm_update.connect(wrapper, sender=model, weak=False) + inst = badge() -class PostCountableAbstractBadge(CountableAbstractBadge): - def __init__(self, model, field, expected_value): + def hook(action, new): + user = inst.award_to(action) - def handler(instance): - self.award_badge(instance.author, instance) + if user: + badge.award(user, action, badge.award_once) - super(PostCountableAbstractBadge, self).__init__(model, field, expected_value, handler) + for action in badge.listen_to: + action.hook(hook) -class NodeCountableAbstractBadge(CountableAbstractBadge): - def __init__(self, node_type, field, expected_value): + BadgesMeta.by_class[name] = badge + badge.ondb.__dict__['_class'] = inst + BadgesMeta.by_id[badge.ondb.id] = badge - def handler(instance): - if instance.node_type == node_type: - self.award_badge(instance.author, instance) + return badge - super(NodeCountableAbstractBadge, self).__init__(Node, field, expected_value, handler) - -class ActivityAbstractBadge(AbstractBadge): - - def __init__(self, activity_type, handler): - - def wrapper(sender, **kwargs): - handler(instance=kwargs['instance']) - - activity_record.connect(wrapper, sender=activity_type, weak=False) - - -class ActivityCountAbstractBadge(AbstractBadge): +class AbstractBadge(object): + __metaclass__ = BadgesMeta - def __init__(self, activity_type, count): + abstract = True + award_once = False - def handler(sender, **kwargs): - instance = kwargs['instance'] - if Activity.objects.filter(user=instance.user, activity_type__in=activity_type).count() == count: - self.award_badge(instance.user, instance.content_object) + @property + def name(self): + raise NotImplementedError - if not isinstance(activity_type, (tuple, list)): - activity_type = (activity_type, ) + @property + def description(self): + raise NotImplementedError - for type in activity_type: - activity_record.connect(handler, sender=type, weak=False) + @classmethod + def award(cls, user, action, once=False): + if once: + node = None + else: + node = action.node -class FirstActivityAbstractBadge(ActivityCountAbstractBadge): + awarded = AwardAction.get_for(user, node, cls.ondb) - def __init__(self, activity_type): - super(FirstActivityAbstractBadge, self).__init__(activity_type, 1) + if not awarded: + AwardAction(user=user, node=node, ip=action.ip).save(data=dict(badge=cls.ondb, trigger=action)) \ No newline at end of file diff --git a/forum/feed.py b/forum/feed.py index 0a08825..4017d61 100644 --- a/forum/feed.py +++ b/forum/feed.py @@ -34,7 +34,7 @@ class RssLastestQuestionsFeed(Feed): return item.added_at def items(self, item): - return Question.objects.filter(deleted=False).order_by('-last_activity_at')[:30] + return Question.objects.filter(deleted=None).order_by('-last_activity_at')[:30] def main(): pass diff --git a/forum/forms.py b/forum/forms.py index 35c6083..70466b5 100644 --- a/forum/forms.py +++ b/forum/forms.py @@ -101,18 +101,6 @@ class SummaryField(forms.CharField): self.label = _('update summary:') self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)') -class ModerateUserForm(forms.ModelForm): - is_approved = forms.BooleanField(label=_("Automatically accept user's contributions for the email updates"), - required=False) - - def clean_is_approved(self): - if 'is_approved' not in self.cleaned_data: - self.cleaned_data['is_approved'] = False - return self.cleaned_data['is_approved'] - - class Meta: - model = User - fields = ('is_approved',) class FeedbackForm(forms.Form): name = forms.CharField(label=_('Your name:'), required=False) diff --git a/forum/management/__init__.py b/forum/management/__init__.py index 49ad8d5..b654caa 100644 --- a/forum/management/__init__.py +++ b/forum/management/__init__.py @@ -1,13 +1,3 @@ from forum.modules import get_modules_script -from django.db.models.signals import post_syncdb -import forum.models - -def setup_badges(sender, **kwargs): - from forum.badges import ALL_BADGES - - for badge in ALL_BADGES: - badge.install() - -post_syncdb.connect(setup_badges, sender=forum.models) get_modules_script('management') \ No newline at end of file diff --git a/forum/migrations/0014_auto__add_field_question_accepted_answer.py b/forum/migrations/0014_auto__add_field_question_accepted_answer.py index 54ef078..e73c960 100644 --- a/forum/migrations/0014_auto__add_field_question_accepted_answer.py +++ b/forum/migrations/0014_auto__add_field_question_accepted_answer.py @@ -12,10 +12,9 @@ class Migration(SchemaMigration): # Adding field 'Question.accepted_answer' db.add_column(u'question', 'accepted_answer', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['forum.Answer'], unique=True, null=True), keep_default=False) - if db.backend_name == "postgres" and not "pgfulltext" in settings.DISABLED_MODULES: - pass - db.execute("DROP TRIGGER tsvectorupdate ON question;") - db.execute("ALTER TABLE question DROP COLUMN tsv;") + #if db.backend_name == "postgres" and not "pgfulltext" in settings.DISABLED_MODULES: + # db.execute("DROP TRIGGER tsvectorupdate ON question;") + # db.execute("ALTER TABLE question DROP COLUMN tsv;") def backwards(self, orm): diff --git a/forum/migrations/0022_auto__add_actionrepute__add_action__add_favoritenode__del_field_node_v.py b/forum/migrations/0022_auto__add_actionrepute__add_action__add_favoritenode__del_field_node_v.py new file mode 100644 index 0000000..87b792e --- /dev/null +++ b/forum/migrations/0022_auto__add_actionrepute__add_action__add_favoritenode__del_field_node_v.py @@ -0,0 +1,463 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + print "# Adding model 'ActionRepute'" + db.create_table('forum_actionrepute', ( + ('action', self.gf('django.db.models.fields.related.ForeignKey')(related_name='reputes', to=orm['forum.Action'])), + ('by_canceled', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('value', self.gf('django.db.models.fields.IntegerField')(default=0)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.User'])), + )) + db.send_create_signal('forum', ['ActionRepute']) + + print "# Adding model 'Action'" + db.create_table('forum_action', ( + ('node', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Node'], null=True)), + ('extra', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('canceled_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='canceled_actions', null=True, to=orm['forum.User'])), + ('canceled', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('action_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='actions', to=orm['forum.User'])), + ('action_type', self.gf('django.db.models.fields.CharField')(max_length=16)), + ('canceled_at', self.gf('django.db.models.fields.DateTimeField')(null=True)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('ip', self.gf('django.db.models.fields.CharField')(max_length=16, null=True)), + )) + db.send_create_signal('forum', ['Action']) + + print "# Deleting field 'Node.vote_up_count'" + db.delete_column('forum_node', 'vote_up_count') + + print "# Deleting field 'Node.comment_count'" + db.delete_column('forum_node', 'comment_count') + + print "# Deleting field 'Node.offensive_flag_count'" + db.delete_column('forum_node', 'offensive_flag_count') + + print "# Deleting field 'Node.vote_down_count'" + db.delete_column('forum_node', 'vote_down_count') + + print "# Adding field 'Node.wiki'" + db.add_column('forum_node', 'wiki', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + print "# Adding field 'Node.marked'" + db.add_column('forum_node', 'marked', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + print "# Adding field 'Node.extra_count'" + db.add_column('forum_node', 'extra_count', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False) + + print "# Adding field 'Node.last_activity_by'" + db.add_column('forum_node', 'last_activity_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.User'], null=True), keep_default=False) + + print "# Adding field 'Node.extra_ref'" + db.add_column('forum_node', 'extra_ref', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Node'], null=True), keep_default=False) + + print "# Adding field 'Node.last_activity_at'" + db.add_column('forum_node', 'last_activity_at', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), keep_default=False) + + # Changing field 'Answer.node_ptr' + #db.alter_column(u'answer', 'node_ptr_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Node'], null=True, primary_key=True)) + + print "# Changing field 'QuestionSubscription.question'" + db.alter_column('forum_questionsubscription', 'question_id', self.gf('django.db.models.fields.IntegerField')()) + + # Removing unique constraint on 'Award', fields ['badge', 'object_id', 'content_type', 'user'] + #db.delete_unique(u'award', ['badge_id', 'object_id', 'content_type_id', 'user_id']) + + print "# Changing field 'User.gold'" + db.alter_column('forum_user', 'gold', self.gf('django.db.models.fields.PositiveIntegerField')()) + + print "# Changing field 'User.silver'" + db.alter_column('forum_user', 'silver', self.gf('django.db.models.fields.PositiveIntegerField')()) + + print "# Changing field 'User.bronze'" + db.alter_column('forum_user', 'bronze', self.gf('django.db.models.fields.PositiveIntegerField')()) + + print "# Deleting field 'Question.answer_count'" + db.delete_column(u'question', 'answer_count') + + print "# Deleting field 'Question.favourite_count'" + db.delete_column(u'question', 'favourite_count') + + # Changing field 'Question.node_ptr' + #db.alter_column(u'question', 'node_ptr_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Node'], null=True, primary_key=True)) + + + def backwards(self, orm): + + # Deleting model 'ActionRepute' + db.delete_table('forum_actionrepute') + + # Deleting model 'Action' + db.delete_table('forum_action') + + # Deleting model 'FavoriteNode' + db.delete_table('forum_favoritenode') + + # Adding field 'Node.vote_up_count' + db.add_column('forum_node', 'vote_up_count', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False) + + # Adding field 'Node.comment_count' + db.add_column('forum_node', 'comment_count', self.gf('django.db.models.fields.PositiveIntegerField')(default=0), keep_default=False) + + # Adding field 'Node.offensive_flag_count' + db.add_column('forum_node', 'offensive_flag_count', self.gf('django.db.models.fields.SmallIntegerField')(default=0), keep_default=False) + + # Adding field 'Node.vote_down_count' + db.add_column('forum_node', 'vote_down_count', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False) + + # Deleting field 'Node.wiki' + db.delete_column('forum_node', 'wiki') + + # Deleting field 'Node.marked' + db.delete_column('forum_node', 'marked') + + # Deleting field 'Node.extra_count' + db.delete_column('forum_node', 'extra_count') + + # Deleting field 'Node.last_activity_by' + db.delete_column('forum_node', 'last_activity_by_id') + + # Deleting field 'Node.extra_ref' + db.delete_column('forum_node', 'extra_ref_id') + + # Deleting field 'Node.last_activity_at' + db.delete_column('forum_node', 'last_activity_at') + + # Changing field 'Answer.node_ptr' + db.alter_column(u'answer', 'node_ptr_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['forum.Node'], unique=True, primary_key=True)) + + # Adding unique constraint on 'Award', fields ['badge', 'object_id', 'content_type', 'user'] + db.create_unique(u'award', ['badge_id', 'object_id', 'content_type_id', 'user_id']) + + # Changing field 'User.gold' + db.alter_column('forum_user', 'gold', self.gf('django.db.models.fields.SmallIntegerField')()) + + # Changing field 'User.silver' + db.alter_column('forum_user', 'silver', self.gf('django.db.models.fields.SmallIntegerField')()) + + # Changing field 'User.bronze' + db.alter_column('forum_user', 'bronze', self.gf('django.db.models.fields.SmallIntegerField')()) + + # Adding field 'Question.answer_count' + db.add_column(u'question', 'answer_count', self.gf('django.db.models.fields.PositiveIntegerField')(default=0), keep_default=False) + + # Adding field 'Question.favourite_count' + db.add_column(u'question', 'favourite_count', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False) + + # Changing field 'Question.node_ptr' + db.alter_column(u'question', 'node_ptr_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['forum.Node'], unique=True)) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.activity': { + 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"}, + 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.anonymousnode': { + 'Meta': {'object_name': 'AnonymousNode', '_ormbases': ['forum.Node']}, + 'convertible_to': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['forum.Node']", 'unique': 'True', 'primary_key': 'True'}), + 'validation_hash': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_content'", 'to': "orm['forum.Node']"}) + }, + 'forum.answer': { + 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"}, + 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'accepted_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'node_ptr': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True', 'primary_key': 'True'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.favoritequestion': { + 'Meta': {'unique_together': "(('question', 'user'),)", 'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'favourites'", 'to': "orm['forum.Question']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['forum.User']"}) + }, + 'forum.flaggeditem': { + 'Meta': {'object_name': 'FlaggedItem', 'db_table': "u'flagged_item'"}, + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'to': "orm['forum.User']"}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_nodes'", 'null': 'True', 'to': "orm['forum.User']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_nodes'", 'null': 'True', 'to': "orm['forum.User']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.question': { + 'Meta': {'object_name': 'Question', 'db_table': "u'question'"}, + 'accepted_answer': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'question_accepting'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Answer']"}), + 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'through': "'FavoriteQuestion'", 'to': "orm['forum.User']"}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'node_ptr': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True', 'primary_key': 'True'}), + 'view_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 27, 11, 37, 29, 356000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.repute': { + 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"}, + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Question']"}), + 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'user_previous_rep': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'value': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 28, 11, 37, 29, 624000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.vote': { + 'Meta': {'object_name': 'Vote', 'db_table': "u'vote'"}, + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.User']"}), + 'vote': ('django.db.models.fields.SmallIntegerField', [], {}), + 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0023_flaten_node_inheritance_create_actions.py b/forum/migrations/0023_flaten_node_inheritance_create_actions.py new file mode 100644 index 0000000..d4b081b --- /dev/null +++ b/forum/migrations/0023_flaten_node_inheritance_create_actions.py @@ -0,0 +1,685 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from forum.migrations import ProgressBar + +GAIN_BY_UPVOTED = 1 +GAIN_BY_ANSWER_ACCEPTED = 2 +GAIN_BY_ACCEPTING_ANSWER = 3 +GAIN_BY_DOWNVOTE_CANCELED = 4 +GAIN_BY_CANCELING_DOWNVOTE = 5 +LOST_BY_CANCELLING_ACCEPTED_ANSWER = -1 +LOST_BY_ACCEPTED_ANSWER_CANCELED = -2 +LOST_BY_DOWNVOTED = -3 +LOST_BY_FLAGGED = -4 +LOST_BY_DOWNVOTING = -5 +LOST_BY_FLAGGED_3_TIMES = -6 +LOST_BY_FLAGGED_5_TIMES = -7 +LOST_BY_UPVOTE_CANCELED = -8 + +class Migration(DataMigration): + + def forwards(self, orm): + rephist = dict([(t, []) for t in range(-8, 6) if t != 0]) + + r_count = orm.Repute.objects.count() + print "\nCalculating rep gain/losses history through %d records:" % r_count + progress = ProgressBar(r_count) + + for r in orm.Repute.objects.all(): + l = rephist.get(r.reputation_type, None) + if l is None: continue + + if (len(l) == 0) or (l[-1][1] != r.value): + l.append((r.reputed_at, r.value)) + + progress.update() + + print "\n...done\n" + + + def repval_at(reptype, repdate, default): + l = rephist.get(reptype, None) + + if l is None: return 0 + if len(l) == 0: return default + + for r in l: + if r[0] <= repdate: + return r[1] or default + + + q_count = orm.Question.objects.count() + print "\nConverting %d questions:" % q_count + progress = ProgressBar(q_count) + + for q in orm.Question.objects.all(): + n = q.node_ptr + n.last_activity_at = q.last_activity_at + n.last_activity_by = q.last_activity_by + + if q.accepted_answer: + n.extra_ref = q.accepted_answer.node_ptr + + n.extra_count = q.view_count + + n.marked = q.closed + n.wiki = q.wiki + + n.save() + + ask = orm.Action( + user = n.author, + action_date = n.added_at, + node = n, + action_type = "ask", + extra = '' + ) + + ask.save() + + if n.deleted: + action = orm.Action( + user = n.deleted_by, + node = n, + action_type = "delete", + action_date = n.deleted_at or datetime.datetime.now(), + extra = '' + ) + + action.save() + + + if n.marked: + action = orm.Action( + user = q.closed_by, + node = n, + action_type = "close", + extra = q.close_reason, + action_date = q.closed_at or datetime.datetime.now(), + ) + + action.save() + + if n.wiki: + action = orm.Action( + user = n.author, + node = n, + action_type = "wikify", + action_date = q.wikified_at or datetime.datetime.now(), + extra = '' + ) + + action.save() + + progress.update() + + print "\n...done\n" + + a_count = orm.Answer.objects.count() + print "\nConverting %d answers:" % a_count + progress = ProgressBar(a_count) + + for a in orm.Answer.objects.all(): + n = a.node_ptr + + n.marked = a.accepted + n.wiki = a.wiki + + n.save() + + ans = orm.Action( + user = n.author, + action_date = n.added_at, + node = n, + action_type = "answer", + extra = '' + ) + + ans.save() + + if n.deleted: + action = orm.Action( + user = n.deleted_by, + node = n, + action_type = "delete", + action_date = n.deleted_at or datetime.datetime.now(), + extra = '' + ) + + action.save() + + if a.accepted: + action = orm.Action( + user = a.accepted_by, + node = n, + action_type = "acceptanswer", + action_date = a.accepted_at or datetime.datetime.now(), + extra = '' + ) + + action.save() + + if not a.wiki or a.wikified_at > action.action_date: + if action.user == n.author: + rep = orm.ActionRepute( + action = action, + user = action.user, + value = repval_at(GAIN_BY_ACCEPTING_ANSWER, action.action_date, 2) + ) + rep.save() + + if n.author != n.parent.author: + rep = orm.ActionRepute( + action = action, + user = n.author, + value = repval_at(GAIN_BY_ANSWER_ACCEPTED, action.action_date, 15) + ) + rep.save() + + if n.wiki: + action = orm.Action( + user = n.author, + node = n, + action_type = "wikify", + action_date = a.wikified_at or datetime.datetime.now(), + extra = '' + ) + + action.save() + + progress.update() + + print "\n...done\n" + + v_count = orm.Vote.objects.count() + print "\nConverting %d votes:" % v_count + progress = ProgressBar(v_count) + + for v in orm.Vote.objects.exclude(canceled=True): + a = orm.Action( + action_type = (v.vote == 1) and ((v.node.node_type == "comment") and "voteupcomment" or "voteup") or "votedown", + user = v.user, + node = v.node, + action_date = v.voted_at, + canceled = v.canceled, + extra = '' + ) + + a.save() + + def impl(node): + if node.node_type == "question": + return orm.Question.objects.get(node_ptr=node) + else: + return orm.Answer.objects.get(node_ptr=node) + + if a.node.node_type in ("question", "answer") and (not a.node.wiki or impl(a.node).wikified_at > a.action_date): + reptype, default = (v.vote == 1) and (GAIN_BY_UPVOTED, 10) or (LOST_BY_DOWNVOTED, 2) + rep = orm.ActionRepute( + action = a, + user = a.node.author, + value = repval_at(reptype, a.action_date, default) or default + ) + rep.save() + + if v.vote == -1: + rep = orm.ActionRepute( + action = a, + user = a.node.author, + value = repval_at(LOST_BY_DOWNVOTING, a.action_date, 1) or default + ) + rep.save() + + progress.update() + + print "\n...done\n" + + f_count = orm.FlaggedItem.objects.count() + print "\nConverting %d flags:" % f_count + progress = ProgressBar(f_count) + + for f in orm.FlaggedItem.objects.all(): + a = orm.Action( + action_type = "flag", + user = f.user, + node = f.node, + action_date = f.flagged_at, + extra = f.reason or '' + ) + + a.save() + + rep = orm.ActionRepute( + action = a, + user = a.node.author, + value = repval_at(LOST_BY_FLAGGED, a.action_date, 2) or 2 + ) + rep.save() + + progress.update() + + print "\n...done\n" + + n_count = orm.Node.objects.all().count() + print "\nChecking flag count of %d nodes:" % n_count + progress = ProgressBar(n_count) + + for n in orm.Node.objects.all(): + flags = list(orm.Action.objects.filter(action_type="flag", node=n, canceled=False).order_by('-action_date')) + + if len(flags) >= 3: + a = flags[2] + rep = orm.ActionRepute( + action = a, + user = n.author, + value = repval_at(LOST_BY_FLAGGED_3_TIMES, a.action_date, 30) + ) + rep.save() + + + if len(flags) >= 5: + a = flags[4] + rep = orm.ActionRepute( + action = a, + user = n.author, + value = repval_at(LOST_BY_FLAGGED_5_TIMES, a.action_date, 100) + ) + rep.save() + + progress.update() + + print "\n...done\n" + + c_count = orm.Node.objects.filter(node_type="comment").count() + print "\nCreating %d comment actions:" % c_count + progress = ProgressBar(c_count) + + for c in orm.Node.objects.filter(node_type="comment").all(): + a = orm.Action( + action_type = "comment", + user = c.author, + node = c, + action_date = c.added_at, + extra = '' + ) + + a.save() + + if c.deleted: + action = orm.Action( + user = c.deleted_by, + node = c, + action_type = "delete", + action_date = c.deleted_at or datetime.datetime.now(), + extra = '' + ) + + action.save() + + progress.update() + + print "\n...done\n" + + + r_count = orm.NodeRevision.objects.exclude(revision=1).count() + print "\nCreating %d edit actions:" % r_count + progress = ProgressBar(r_count) + + for r in orm.NodeRevision.objects.exclude(revision=1): + a = orm.Action( + action_type = "revise", + user = r.author, + node = r.node, + action_date = r.revised_at, + extra = r.revision + ) + + a.save() + progress.update() + + print "\n...done\n" + + u_count = orm.User.objects.all().count() + print "\nCreating %d user join actions and reputation recalculation:" % u_count + progress = ProgressBar(u_count) + + for u in orm.User.objects.all(): + a = orm.Action( + user = u, + action_date = u.date_joined, + action_type = "userjoins", + ) + + a.save() + + rep = orm.ActionRepute( + action = a, + user = u, + value = 1 + ) + rep.save() + + new_rep = orm.ActionRepute.objects.filter(user=u).aggregate(reputation=models.Sum('value'))['reputation'] + + if new_rep < 0: + new_rep = 1 + + u.reputation = new_rep + u.save() + + progress.update() + + print "\n...done\n" + + + + def backwards(self, orm): + "Write your backwards methods here." + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.activity': { + 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"}, + 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.anonymousnode': { + 'Meta': {'object_name': 'AnonymousNode', '_ormbases': ['forum.Node']}, + 'convertible_to': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'node_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['forum.Node']", 'unique': 'True', 'primary_key': 'True'}), + 'validation_hash': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_content'", 'to': "orm['forum.Node']"}) + }, + 'forum.answer': { + 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"}, + 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'accepted_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'node_ptr': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True', 'primary_key': 'True'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.favoritequestion': { + 'Meta': {'unique_together': "(('question', 'user'),)", 'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'favourites'", 'to': "orm['forum.Question']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['forum.User']"}) + }, + 'forum.flaggeditem': { + 'Meta': {'object_name': 'FlaggedItem', 'db_table': "u'flagged_item'"}, + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'to': "orm['forum.User']"}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_nodes'", 'null': 'True', 'to': "orm['forum.User']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_nodes'", 'null': 'True', 'to': "orm['forum.User']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.question': { + 'Meta': {'object_name': 'Question', 'db_table': "u'question'"}, + 'accepted_answer': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'question_accepting'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Answer']"}), + 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'through': "'FavoriteQuestion'", 'to': "orm['forum.User']"}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'node_ptr': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True', 'primary_key': 'True'}), + 'view_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 27, 11, 40, 32, 68000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.repute': { + 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"}, + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Question']"}), + 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'user_previous_rep': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'value': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 28, 11, 40, 32, 153000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.vote': { + 'Meta': {'object_name': 'Vote', 'db_table': "u'vote'"}, + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.User']"}), + 'vote': ('django.db.models.fields.SmallIntegerField', [], {}), + 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0024_auto__del_repute__del_vote__del_answer__del_flaggeditem__del_anonymous.py b/forum/migrations/0024_auto__del_repute__del_vote__del_answer__del_flaggeditem__del_anonymous.py new file mode 100644 index 0000000..38a7604 --- /dev/null +++ b/forum/migrations/0024_auto__del_repute__del_vote__del_answer__del_flaggeditem__del_anonymous.py @@ -0,0 +1,377 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting model 'Repute' + db.delete_table(u'repute') + + # Deleting model 'Vote' + db.delete_table(u'vote') + + # Deleting model 'Answer' + db.delete_table(u'answer') + + # Deleting model 'FlaggedItem' + db.delete_table(u'flagged_item') + + # Deleting model 'AnonymousNode' + db.delete_table('forum_anonymousnode') + + # Deleting model 'FavoriteQuestion' + db.delete_table(u'favorite_question') + + # Deleting model 'Question' + db.delete_table(u'question') + + # Deleting field 'Node.deleted_at' + db.delete_column('forum_node', 'deleted_at') + + # Deleting field 'Node.last_edited_by' + db.delete_column('forum_node', 'last_edited_by_id') + + # Deleting field 'Node.deleted' + db.delete_column('forum_node', 'deleted') + + # Deleting field 'Node.deleted_by' + db.delete_column('forum_node', 'deleted_by_id') + + # Deleting field 'Node.last_edited_at' + db.delete_column('forum_node', 'last_edited_at') + + + def backwards(self, orm): + + # Adding model 'Repute' + db.create_table(u'repute', ( + ('node', self.gf('django.db.models.fields.related.ForeignKey')(related_name='reputes', null=True, to=orm['forum.Node'])), + ('reputed_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('question', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Question'])), + ('value', self.gf('django.db.models.fields.SmallIntegerField')(default=0)), + ('canceled', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('reputation_type', self.gf('django.db.models.fields.SmallIntegerField')()), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='reputes', to=orm['forum.User'])), + ('user_previous_rep', self.gf('django.db.models.fields.IntegerField')(default=0)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('forum', ['Repute']) + + # Adding model 'Vote' + db.create_table(u'vote', ( + ('node', self.gf('django.db.models.fields.related.ForeignKey')(related_name='votes', null=True, to=orm['forum.Node'])), + ('vote', self.gf('django.db.models.fields.SmallIntegerField')()), + ('canceled', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='votes', to=orm['forum.User'])), + ('voted_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('forum', ['Vote']) + + # Adding model 'Answer' + db.create_table(u'answer', ( + ('wiki', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('accepted_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.User'], null=True)), + ('accepted_at', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('wikified_at', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('node_ptr', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Node'], null=True, primary_key=True)), + ('accepted', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + )) + db.send_create_signal('forum', ['Answer']) + + # Adding model 'FlaggedItem' + db.create_table(u'flagged_item', ( + ('node', self.gf('django.db.models.fields.related.ForeignKey')(related_name='flaggeditems', null=True, to=orm['forum.Node'])), + ('flagged_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('canceled', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('reason', self.gf('django.db.models.fields.CharField')(max_length=300, null=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='flaggeditems', to=orm['forum.User'])), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('forum', ['FlaggedItem']) + + # Adding model 'AnonymousNode' + db.create_table('forum_anonymousnode', ( + ('convertible_to', self.gf('django.db.models.fields.CharField')(default='node', max_length=16)), + ('validation_hash', self.gf('django.db.models.fields.related.ForeignKey')(related_name='anonymous_content', to=orm['forum.Node'])), + ('node_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['forum.Node'], unique=True, primary_key=True)), + )) + db.send_create_signal('forum', ['AnonymousNode']) + + # Adding model 'FavoriteQuestion' + db.create_table(u'favorite_question', ( + ('question', self.gf('django.db.models.fields.related.ForeignKey')(related_name='favourites', to=orm['forum.Question'])), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('added_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='user_favorite_questions', to=orm['forum.User'])), + )) + db.send_create_signal('forum', ['FavoriteQuestion']) + + # Adding model 'Question' + db.create_table(u'question', ( + ('wiki', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('last_activity_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='last_active_in_questions', null=True, to=orm['forum.User'])), + ('close_reason', self.gf('django.db.models.fields.SmallIntegerField')(null=True, blank=True)), + ('last_activity_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('view_count', self.gf('django.db.models.fields.IntegerField')(default=0)), + ('node_ptr', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Node'], null=True, primary_key=True)), + ('accepted_answer', self.gf('django.db.models.fields.related.OneToOneField')(related_name='question_accepting', unique=True, null=True, to=orm['forum.Answer'])), + ('closed', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('wikified_at', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('closed_at', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('closed_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='closed_questions', null=True, to=orm['forum.User'], blank=True)), + )) + db.send_create_signal('forum', ['Question']) + + # Adding field 'Node.deleted_at' + db.add_column('forum_node', 'deleted_at', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), keep_default=False) + + # Adding field 'Node.last_edited_by' + db.add_column('forum_node', 'last_edited_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='last_edited_nodes', null=True, to=orm['forum.User'], blank=True), keep_default=False) + + # Adding field 'Node.deleted' + db.add_column('forum_node', 'deleted', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + # Adding field 'Node.deleted_by' + db.add_column('forum_node', 'deleted_by', self.gf('django.db.models.fields.related.ForeignKey')(related_name='deleted_nodes', null=True, to=orm['forum.User'], blank=True), keep_default=False) + + # Adding field 'Node.last_edited_at' + db.add_column('forum_node', 'last_edited_at', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.activity': { + 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"}, + 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 28, 23, 43, 52, 301000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 29, 23, 43, 52, 512000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0025_auto__add_field_node_extra_action__add_field_node_deleted__add_field_n.py b/forum/migrations/0025_auto__add_field_node_extra_action__add_field_node_deleted__add_field_n.py new file mode 100644 index 0000000..2cf638c --- /dev/null +++ b/forum/migrations/0025_auto__add_field_node_extra_action__add_field_node_deleted__add_field_n.py @@ -0,0 +1,267 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'Node.extra_action' + db.add_column('forum_node', 'extra_action', self.gf('django.db.models.fields.related.ForeignKey')(related_name='extra_node', null=True, to=orm['forum.Action']), keep_default=False) + + # Adding field 'Node.deleted' + db.add_column('forum_node', 'deleted', self.gf('django.db.models.fields.related.ForeignKey')(related_name='deleted_node', unique=True, null=True, to=orm['forum.Action']), keep_default=False) + + # Adding field 'Node.last_edited' + db.add_column('forum_node', 'last_edited', self.gf('django.db.models.fields.related.ForeignKey')(related_name='edited_node', unique=True, null=True, to=orm['forum.Action']), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Node.extra_action' + db.delete_column('forum_node', 'extra_action_id') + + # Deleting field 'Node.deleted' + db.delete_column('forum_node', 'deleted_id') + + # Deleting field 'Node.last_edited' + db.delete_column('forum_node', 'last_edited_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.activity': { + 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"}, + 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 28, 23, 49, 37, 322000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 29, 23, 49, 37, 506000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0026_reset_deleted_and_lastedited_flags.py b/forum/migrations/0026_reset_deleted_and_lastedited_flags.py new file mode 100644 index 0000000..8521ffd --- /dev/null +++ b/forum/migrations/0026_reset_deleted_and_lastedited_flags.py @@ -0,0 +1,278 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from forum.migrations import ProgressBar + +class Migration(DataMigration): + + def forwards(self, orm): + n_count = orm.Node.objects.all().count() + print "\nReseting %d nodes:" % n_count + progress = ProgressBar(n_count) + + for n in orm.Node.objects.all(): + try: + d = orm.Action.objects.get(node=n, action_type="delete", canceled=False) + n.deleted_id = d.id + except Exception, e: + n.deleted = None + + if orm.Action.objects.filter(node=n, action_type="revise").count() > 0: + n.last_edited_id = orm.Action.objects.filter(node=n, action_type="revise").order_by('-action_date')[0].id + else: + n.last_edited = None + + + if n.node_type == "answer" and n.marked: + n.extra_action_id = orm.Action.objects.get(node=n, action_type="acceptanswer", canceled=False).id + + if n.node_type == "question" and n.marked: + n.extra_action_id = orm.Action.objects.get(node=n, action_type="close", canceled=False).id + + n.save() + + progress.update() + + print "\n...done\n" + + + def backwards(self, orm): + "Write your backwards methods here." + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.activity': { + 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"}, + 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 28, 23, 55, 36, 647000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 29, 23, 55, 36, 708000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0027_auto__del_activity.py b/forum/migrations/0027_auto__del_activity.py new file mode 100644 index 0000000..589b5bb --- /dev/null +++ b/forum/migrations/0027_auto__del_activity.py @@ -0,0 +1,254 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting model 'Activity' + db.delete_table(u'activity') + + + def backwards(self, orm): + + # Adding model 'Activity' + db.create_table(u'activity', ( + ('is_auditted', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)), + ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.User'])), + ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])), + ('active_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('activity_type', self.gf('django.db.models.fields.SmallIntegerField')()), + )) + db.send_create_signal('forum', ['Activity']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 29, 1, 30, 30, 35000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 30, 1, 30, 30, 211000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0028_auto__add_field_action_canceled_ip__add_field_actionrepute_date.py b/forum/migrations/0028_auto__add_field_action_canceled_ip__add_field_actionrepute_date.py new file mode 100644 index 0000000..1170bf7 --- /dev/null +++ b/forum/migrations/0028_auto__add_field_action_canceled_ip__add_field_actionrepute_date.py @@ -0,0 +1,253 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'Action.canceled_ip' + db.add_column('forum_action', 'canceled_ip', self.gf('django.db.models.fields.CharField')(default='', max_length=16), keep_default=False) + + # Adding field 'ActionRepute.date' + db.add_column('forum_actionrepute', 'date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Action.canceled_ip' + db.delete_column('forum_action', 'canceled_ip') + + # Deleting field 'ActionRepute.date' + db.delete_column('forum_actionrepute', 'date') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 29, 21, 20, 24, 880000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 30, 21, 20, 35, 361000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0029_repute_dates.py b/forum/migrations/0029_repute_dates.py new file mode 100644 index 0000000..14d6e62 --- /dev/null +++ b/forum/migrations/0029_repute_dates.py @@ -0,0 +1,259 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from forum.migrations import ProgressBar + +class Migration(DataMigration): + + def forwards(self, orm): + r_count = orm.ActionRepute.objects.count() + print "\nAdding dates to %d repute actions:" % r_count + progress = ProgressBar(r_count) + + for r in orm.ActionRepute.objects.all(): + a = r.action + + if r.by_canceled: + r.date = a.canceled_at + else: + r.date = a.action_date + + r.save() + + progress.update() + + print "\n...done\n" + + + def backwards(self, orm): + "Write your backwards methods here." + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'extra': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 29, 21, 21, 16, 237000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 30, 21, 21, 16, 298000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0030_auto__chg_field_action_extra__chg_field_keyvalue_value.py b/forum/migrations/0030_auto__chg_field_action_extra__chg_field_keyvalue_value.py new file mode 100644 index 0000000..cd4e1f1 --- /dev/null +++ b/forum/migrations/0030_auto__chg_field_action_extra__chg_field_keyvalue_value.py @@ -0,0 +1,253 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Action.extra' + db.alter_column('forum_action', 'extra', self.gf('forum.models.utils.PickledObjectField')(null=True)) + + # Changing field 'KeyValue.value' + db.alter_column('forum_keyvalue', 'value', self.gf('forum.models.utils.PickledObjectField')(null=True)) + + + def backwards(self, orm): + + # Changing field 'Action.extra' + db.alter_column('forum_action', 'extra', self.gf('django.db.models.fields.CharField')(max_length=255)) + + # Changing field 'KeyValue.value' + db.alter_column('forum_keyvalue', 'value', self.gf('forum.models.utils.PickledObjectField')()) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 4, 30, 23, 58, 8, 677000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 1, 23, 58, 8, 841000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0031_alter_pickle_storage.py b/forum/migrations/0031_alter_pickle_storage.py new file mode 100644 index 0000000..5c8bb7b --- /dev/null +++ b/forum/migrations/0031_alter_pickle_storage.py @@ -0,0 +1,274 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from forum.migrations import ProgressBar +from forum.models.utils import dbsafe_encode + +try: + from cPickle import loads, dumps +except ImportError: + from pickle import loads, dumps + +class Migration(DataMigration): + + def forwards(self, orm): + k_count = orm.KeyValue.objects.count() + print "\nConverting %d keyvalue objects:" % k_count + progress = ProgressBar(k_count) + + for kv in orm.KeyValue.objects.all(): + try: + o = loads(kv.value.encode('utf-8')) + except: + o = kv.value + + kv.value = dbsafe_encode(o, compress_object=True) + kv.save() + progress.update() + + print "\n...done\n" + + a_count = orm.Action.objects.count() + print "\nConverting %d actions extra fields:" % a_count + progress = ProgressBar(a_count) + + for a in orm.Action.objects.all(): + a.extra = dbsafe_encode(a.extra, compress_object=True) + a.save() + progress.update() + + print "\n...done\n" + + + def backwards(self, orm): + "Write your backwards methods here." + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 1, 0, 0, 32, 37000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 2, 0, 0, 32, 86000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0032_auto__del_field_user_hide_ignored_questions__del_field_user_questions_.py b/forum/migrations/0032_auto__del_field_user_hide_ignored_questions__del_field_user_questions_.py new file mode 100644 index 0000000..7a15772 --- /dev/null +++ b/forum/migrations/0032_auto__del_field_user_hide_ignored_questions__del_field_user_questions_.py @@ -0,0 +1,263 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'User.hide_ignored_questions' + db.delete_column('forum_user', 'hide_ignored_questions') + + # Deleting field 'User.questions_per_page' + db.delete_column('forum_user', 'questions_per_page') + + # Deleting field 'User.email_key' + db.delete_column('forum_user', 'email_key') + + # Adding field 'Node.in_moderation' + db.add_column('forum_node', 'in_moderation', self.gf('django.db.models.fields.related.ForeignKey')(related_name='moderated_node', unique=True, null=True, to=orm['forum.Action']), keep_default=False) + + + def backwards(self, orm): + + # Adding field 'User.hide_ignored_questions' + db.add_column('forum_user', 'hide_ignored_questions', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + # Adding field 'User.questions_per_page' + db.add_column('forum_user', 'questions_per_page', self.gf('django.db.models.fields.SmallIntegerField')(default=10), keep_default=False) + + # Adding field 'User.email_key' + db.add_column('forum_user', 'email_key', self.gf('django.db.models.fields.CharField')(max_length=32, null=True), keep_default=False) + + # Deleting field 'Node.in_moderation' + db.delete_column('forum_node', 'in_moderation_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'object_name': 'Award', 'db_table': "u'award'"}, + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_moderation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'moderated_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 2, 4, 54, 13, 72000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 3, 4, 54, 13, 256000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0033_auto__add_flag__add_vote__add_field_badge_cls__del_unique_badge_type_n.py b/forum/migrations/0033_auto__add_flag__add_vote__add_field_badge_cls__del_unique_badge_type_n.py new file mode 100644 index 0000000..f2b38e8 --- /dev/null +++ b/forum/migrations/0033_auto__add_flag__add_vote__add_field_badge_cls__del_unique_badge_type_n.py @@ -0,0 +1,333 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + db.rename_table('award', 'forum_award') + db.rename_table('badge', 'forum_badge') + db.rename_table('tag', 'forum_tag') + + # Adding model 'Flag' + db.create_table('forum_flag', ( + ('node', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Node'])), + ('action', self.gf('django.db.models.fields.related.ForeignKey')(related_name='flag', unique=True, to=orm['forum.Action'])), + ('reason', self.gf('django.db.models.fields.CharField')(max_length=300)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.User'])), + ('flagged_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + )) + #db.send_create_signal('forum', ['Flag']) + + # Adding model 'Vote' + db.create_table('forum_vote', ( + ('node', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.Node'])), + ('action', self.gf('django.db.models.fields.related.ForeignKey')(related_name='vote', unique=True, to=orm['forum.Action'])), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('value', self.gf('django.db.models.fields.SmallIntegerField')()), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['forum.User'])), + ('voted_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + )) + #db.send_create_signal('forum', ['Vote']) + + # Adding field 'Badge.cls' + db.add_column('forum_badge', 'cls', self.gf('django.db.models.fields.CharField')(max_length=50, null=True), keep_default=False) + + # Removing unique constraint on 'Badge', fields ['type', 'name'] + db.delete_unique(u'forum_badge', ['type', 'name']) + + # Deleting field 'Award.notified' + db.delete_column(u'forum_award', 'notified') + + # Adding field 'Award.node' + db.add_column('forum_award', 'node', self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['forum.Node'], null=True), keep_default=False) + + # Adding field 'Award.trigger' + db.add_column('forum_award', 'trigger', self.gf('django.db.models.fields.related.ForeignKey')(related_name='awards', null=True, to=orm['forum.Action']), keep_default=False) + + # Adding field 'Award.action' + db.add_column('forum_award', 'action', self.gf('django.db.models.fields.related.ForeignKey')(default=1, related_name='award', to=orm['forum.Action']), keep_default=False) + + # Adding unique constraint on 'Award', fields ['node', 'badge', 'user'] + #db.create_unique('forum_award', ['node_id', 'badge_id', 'user_id']) + + + def backwards(self, orm): + + # Deleting model 'Flag' + db.delete_table('forum_flag') + + # Deleting model 'Vote' + db.delete_table('forum_vote') + + # Deleting field 'Badge.cls' + db.delete_column('forum_badge', 'cls') + + # Adding unique constraint on 'Badge', fields ['type', 'name'] + db.create_unique(u'badge', ['type', 'name']) + + # Adding field 'Award.notified' + db.add_column(u'award', 'notified', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + # Deleting field 'Award.node' + db.delete_column('forum_award', 'node_id') + + # Deleting field 'Award.trigger' + db.delete_column('forum_award', 'trigger_id') + + # Deleting field 'Award.action' + db.delete_column('forum_award', 'action_id') + + # Removing unique constraint on 'Award', fields ['node', 'badge', 'user'] + db.delete_unique('forum_award', ['node_id', 'badge_id', 'user_id']) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'unique_together': "(('user', 'badge', 'node'),)", 'object_name': 'Award'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award'", 'to': "orm['forum.Action']"}), + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'trigger': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'object_name': 'Badge'}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'cls': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.flag': { + 'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Flag'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flag'", 'unique': 'True', 'to': "orm['forum.Action']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_moderation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'moderated_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 3, 11, 41, 55, 831000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag'}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 4, 11, 41, 59, 140000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.vote': { + 'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Vote'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vote'", 'unique': 'True', 'to': "orm['forum.Action']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.SmallIntegerField', [], {}), + 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0034_new_badge_and_award.py b/forum/migrations/0034_new_badge_and_award.py new file mode 100644 index 0000000..89399f9 --- /dev/null +++ b/forum/migrations/0034_new_badge_and_award.py @@ -0,0 +1,344 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from forum.migrations import ProgressBar +from forum.models.utils import dbsafe_decode + +class Migration(DataMigration): + + def forwards(self, orm): + b_count = orm.Badge.objects.count() + print "\nConverting %d badges:" % b_count + progress = ProgressBar(b_count) + + for b in orm.Badge.objects.all(): + b.cls = "".join([s[0].upper() + s[1:] for s in b.slug.split('-')]) + b.save() + + progress.update() + + print "\n...done\n" + + ctypes = dict([(ct.name, ct.id) for ct in orm['contenttypes.ContentType'].objects.all()]) + + a_count = orm.Award.objects.count() + print "\nConverting %d awards:" % a_count + progress = ProgressBar(a_count) + + for a in orm.Award.objects.all(): + if a.content_type.id == ctypes['user']: + a.node = None + else: + try: + a.node = orm.Node.objects.get(id=a.object_id) + except: + a.delete() + continue + + action = orm.Action( + user = a.user, + node = a.node, + action_type = "award", + action_date = a.awarded_at, + ) + + action.save() + + a.action = action + a.save() + + progress.update() + + print "\n...done\n" + + + a_count = orm.Action.objects.filter(action_type__in=("voteup", "votedown", "voteupcomment")).count() + print "\nConverting %d votes:" % a_count + progress = ProgressBar(a_count) + + for a in orm.Action.objects.filter(action_type__in=("voteup", "votedown", "voteupcomment"), canceled=False): + v = orm.Vote( + user = a.user, + node = a.node, + value = (a.action_type in ("voteup", "voteupcomment")) and 1 or -1, + action = a, + voted_at = a.action_date + ) + + v.save() + + progress.update() + + print "\n...done\n" + + a_count = orm.Action.objects.filter(action_type__in=("voteup", "votedown", "voteupcomment")).count() + print "\nConverting %d votes:" % a_count + progress = ProgressBar(a_count) + + for a in orm.Action.objects.filter(action_type="flag", canceled=False): + f = orm.Flag( + user = a.user, + node = a.node, + reason = a.extra, + action = a, + flagged_at = a.action_date + ) + + f.save() + + progress.update() + + print "\n...done\n" + + + def backwards(self, orm): + "Write your backwards methods here." + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'unique_together': "(('user', 'badge', 'node'),)", 'object_name': 'Award'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award'", 'to': "orm['forum.Action']"}), + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Badge']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'trigger': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'object_name': 'Badge'}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'cls': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.flag': { + 'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Flag'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flag'", 'unique': 'True', 'to': "orm['forum.Action']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_moderation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'moderated_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 3, 11, 43, 54, 540000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag'}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 4, 11, 43, 54, 592000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.vote': { + 'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Vote'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vote'", 'unique': 'True', 'to': "orm['forum.Action']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.SmallIntegerField', [], {}), + 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + } + } + + complete_apps = ['forum'] diff --git a/forum/migrations/0035_auto__del_field_award_object_id__del_field_award_content_type__add_uni.py b/forum/migrations/0035_auto__del_field_award_object_id__del_field_award_content_type__add_uni.py new file mode 100644 index 0000000..b566ef0 --- /dev/null +++ b/forum/migrations/0035_auto__del_field_award_object_id__del_field_award_content_type__add_uni.py @@ -0,0 +1,296 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'Award.object_id' + db.delete_column('forum_award', 'object_id') + + # Deleting field 'Award.content_type' + db.delete_column('forum_award', 'content_type_id') + + # Adding unique constraint on 'Award', fields ['action'] + db.create_unique('forum_award', ['action_id']) + + # Deleting field 'Badge.multiple' + db.delete_column('forum_badge', 'multiple') + + # Deleting field 'Badge.name' + db.delete_column('forum_badge', 'name') + + # Deleting field 'Badge.slug' + db.delete_column('forum_badge', 'slug') + + # Deleting field 'Badge.description' + db.delete_column('forum_badge', 'description') + + + def backwards(self, orm): + + # Adding field 'Award.object_id' + db.add_column('forum_award', 'object_id', self.gf('django.db.models.fields.PositiveIntegerField')(default=1), keep_default=False) + + # Adding field 'Award.content_type' + db.add_column('forum_award', 'content_type', self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['contenttypes.ContentType']), keep_default=False) + + # Removing unique constraint on 'Award', fields ['action'] + db.delete_unique('forum_award', ['action_id']) + + # Adding field 'Badge.multiple' + db.add_column('forum_badge', 'multiple', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + # Adding field 'Badge.name' + db.add_column('forum_badge', 'name', self.gf('django.db.models.fields.CharField')(default=1, max_length=50), keep_default=False) + + # Adding field 'Badge.slug' + db.add_column('forum_badge', 'slug', self.gf('django.db.models.fields.SlugField')(blank=True, default=1, max_length=50, db_index=True), keep_default=False) + + # Adding field 'Badge.description' + db.add_column('forum_badge', 'description', self.gf('django.db.models.fields.CharField')(default=1, max_length=300), keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'forum.action': { + 'Meta': {'object_name': 'Action'}, + 'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}), + 'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"}) + }, + 'forum.actionrepute': { + 'Meta': {'object_name': 'ActionRepute'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}), + 'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'forum.authkeyuserassociation': { + 'Meta': {'object_name': 'AuthKeyUserAssociation'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"}) + }, + 'forum.award': { + 'Meta': {'unique_together': "(('user', 'badge', 'node'),)", 'object_name': 'Award'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award'", 'unique': 'True', 'to': "orm['forum.Action']"}), + 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Badge']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'trigger': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.badge': { + 'Meta': {'object_name': 'Badge'}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}), + 'cls': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'type': ('django.db.models.fields.SmallIntegerField', [], {}) + }, + 'forum.flag': { + 'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Flag'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flag'", 'unique': 'True', 'to': "orm['forum.Action']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + }, + 'forum.keyvalue': { + 'Meta': {'object_name': 'KeyValue'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}) + }, + 'forum.markedtag': { + 'Meta': {'object_name': 'MarkedTag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"}) + }, + 'forum.node': { + 'Meta': {'object_name': 'Node'}, + 'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'deleted': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'deleted_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_node'", 'null': 'True', 'to': "orm['forum.Action']"}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_moderation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'moderated_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}), + 'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}), + 'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}) + }, + 'forum.noderevision': { + 'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}), + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + 'forum.openidassociation': { + 'Meta': {'object_name': 'OpenIdAssociation'}, + 'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}), + 'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'issued': ('django.db.models.fields.IntegerField', [], {}), + 'lifetime': ('django.db.models.fields.IntegerField', [], {}), + 'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}), + 'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'}) + }, + 'forum.openidnonce': { + 'Meta': {'object_name': 'OpenIdNonce'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}), + 'timestamp': ('django.db.models.fields.IntegerField', [], {}) + }, + 'forum.questionsubscription': { + 'Meta': {'object_name': 'QuestionSubscription'}, + 'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 3, 11, 46, 22, 80000)'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.subscriptionsettings': { + 'Meta': {'object_name': 'SubscriptionSettings'}, + 'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'new_question': ('django.db.models.fields.CharField', [], {'default': "'d'", 'max_length': '1'}), + 'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), + 'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"}) + }, + 'forum.tag': { + 'Meta': {'object_name': 'Tag'}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['forum.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'forum.user': { + 'Meta': {'object_name': 'User', '_ormbases': ['auth.User']}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}), + 'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'forum.validationhash': { + 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, + 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 5, 4, 11, 46, 28, 428000)'}), + 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) + }, + 'forum.vote': { + 'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Vote'}, + 'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vote'", 'unique': 'True', 'to': "orm['forum.Action']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}), + 'value': ('django.db.models.fields.SmallIntegerField', [], {}), + 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + } + } + + complete_apps = ['forum'] diff --git a/forum/models/__init__.py b/forum/models/__init__.py index 5b6414a..2c9c997 100644 --- a/forum/models/__init__.py +++ b/forum/models/__init__.py @@ -1,11 +1,11 @@ -from question import Question ,QuestionRevision, FavoriteQuestion, QuestionSubscription +from question import Question ,QuestionRevision, QuestionSubscription from answer import Answer, AnswerRevision from tag import Tag, MarkedTag -from meta import Vote, FlaggedItem -from user import User, Activity, ValidationHash, AuthKeyUserAssociation, SubscriptionSettings -from repute import Badge, Award, Repute -from node import Node, NodeRevision, NodeMetaClass, AnonymousNode +from user import User, ValidationHash, AuthKeyUserAssociation, SubscriptionSettings +from node import Node, NodeRevision, NodeMetaClass from comment import Comment +from action import Action, ActionRepute +from meta import Vote, Flag, Badge, Award from utils import KeyValue try: @@ -16,20 +16,13 @@ except: from base import * -def is_new(sender, instance, **kwargs): - try: - instance._is_new = not bool(instance.id) - except: - pass - -pre_save.connect(is_new) - __all__ = [ - 'Node', 'NodeRevision', 'AnonymousNode', - 'Question', 'FavoriteQuestion', 'QuestionSubscription', 'QuestionRevision', + 'Node', 'NodeRevision', + 'Question', 'QuestionSubscription', 'QuestionRevision', 'Answer', 'AnswerRevision', - 'Tag', 'Comment', 'Vote', 'FlaggedItem', 'MarkedTag', 'Badge', 'Award', 'Repute', - 'Activity', 'ValidationHash', 'AuthKeyUserAssociation', 'SubscriptionSettings', 'KeyValue', 'User', + 'Tag', 'Comment', 'MarkedTag', 'Badge', 'Award', + 'ValidationHash', 'AuthKeyUserAssociation', 'SubscriptionSettings', 'KeyValue', 'User', + 'Action', 'ActionRepute', 'Vote', 'Flag' ] @@ -40,4 +33,5 @@ for k, v in get_modules_script_classes('models', models.Model).items(): __all__.append(k) exec "%s = v" % k -NodeMetaClass.setup_relations() \ No newline at end of file +NodeMetaClass.setup_relations() +BaseMetaClass.setup_denormalizes() \ No newline at end of file diff --git a/forum/models/action.py b/forum/models/action.py new file mode 100644 index 0000000..10eb165 --- /dev/null +++ b/forum/models/action.py @@ -0,0 +1,268 @@ +from django.utils.translation import ugettext as _ +from utils import PickledObjectField +from threading import Thread +from base import * +import re + +class ActionManager(models.Manager): + use_for_related_fields = True + + def get_query_set(self): + qs = super(ActionManager, self).get_query_set().filter(canceled=False) + + if self.model is not Action: + return qs.filter(action_type=self.model.get_type()) + else: + return qs + + def get(self, *args, **kwargs): + action = super(ActionManager, self).get(*args, **kwargs) + if self.model == Action: + return action.leaf() + return action + + def get_for_types(self, types, *args, **kwargs): + kwargs['action_type__in'] = [t.get_type() for t in types] + return self.get(*args, **kwargs) + + + +class Action(models.Model): + user = models.ForeignKey('User', related_name="actions") + ip = models.CharField(max_length=16) + node = models.ForeignKey('Node', null=True, related_name="actions") + action_type = models.CharField(max_length=16) + action_date = models.DateTimeField(default=datetime.datetime.now) + + extra = PickledObjectField() + + canceled = models.BooleanField(default=False) + canceled_by = models.ForeignKey('User', null=True, related_name="canceled_actions") + canceled_at = models.DateTimeField(null=True) + canceled_ip = models.CharField(max_length=16) + + hooks = {} + + objects = ActionManager() + + @property + def at(self): + return self.action_date + + @property + def by(self): + return self.user + + def repute_users(self): + pass + + def process_data(self, **data): + pass + + def process_action(self): + pass + + def cancel_action(self): + pass + + def describe(self, viewer=None): + return "" + + def repute(self, user, value): + repute = ActionRepute(action=self, user=user, value=value) + repute.save() + return repute + + def cancel_reputes(self): + for repute in self.reputes.all(): + cancel = ActionRepute(action=self, user=repute.user, value=(-repute.value), by_canceled=True) + cancel.save() + + def leaf(self): + leaf_cls = ActionProxyMetaClass.types.get(self.action_type, None) + + if leaf_cls is None: + return self + + leaf = leaf_cls() + leaf.__dict__ = self.__dict__ + return leaf + + @classmethod + def get_type(cls): + return re.sub(r'action$', '', cls.__name__.lower()) + + def save(self, data=None, *args, **kwargs): + isnew = False + + if not self.id: + self.action_type = self.__class__.get_type() + isnew = True + + if data: + self.process_data(**data) + + super(Action, self).save(*args, **kwargs) + + if isnew: + if (self.node is None) or (not self.node.wiki): + self.repute_users() + self.process_action() + self.trigger_hooks(True) + + return self + + def delete(self): + self.cancel_action() + super(Action, self).delete() + + def cancel(self, user=None, ip=None): + if not self.canceled: + self.canceled = True + self.canceled_at = datetime.datetime.now() + self.canceled_by = (user is None) and self.user or user + if ip: + self.canceled_ip = ip + self.save() + self.cancel_reputes() + self.cancel_action() + #self.trigger_hooks(False) + + @classmethod + def get_current(cls, **kwargs): + kwargs['canceled'] = False + + try: + return cls.objects.get(**kwargs) + except cls.MultipleObjectsReturned: + logging.error("Got multiple values for action %s with args %s", cls.__name__, + ", ".join(["%s='%s'" % i for i in kwargs.items()])) + raise + except cls.DoesNotExist: + return None + + @classmethod + def hook(cls, fn): + if not Action.hooks.get(cls, None): + Action.hooks[cls] = [] + + Action.hooks[cls].append(fn) + + def trigger_hooks(self, new=True): + thread = Thread(target=trigger_hooks_threaded, args=[self, Action.hooks, new]) + thread.setDaemon(True) + thread.start() + + class Meta: + app_label = 'forum' + +def trigger_hooks_threaded(action, hooks, new): + for cls, hooklist in hooks.items(): + if isinstance(action, cls): + for hook in hooklist: + try: + hook(action=action, new=new) + except Exception, e: + logging.error("Error in %s hook: %s" % (cls.__name__, str(e))) + +class ActionProxyMetaClass(models.Model.__metaclass__): + types = {} + + def __new__(cls, *args, **kwargs): + new_cls = super(ActionProxyMetaClass, cls).__new__(cls, *args, **kwargs) + cls.types[new_cls.get_type()] = new_cls + + class Meta: + proxy = True + + new_cls.Meta = Meta + return new_cls + +class ActionProxy(Action): + __metaclass__ = ActionProxyMetaClass + + def friendly_username(self, viewer, user): + return (viewer == user) and _('You') or user.username + + def friendly_ownername(self, owner, user): + return (owner == user) and _('your') or user.username + + def hyperlink(self, url, title, **attrs): + return '%s' % (url, " ".join('%s="%s"' % i for i in attrs.items()), title) + + def describe_node(self, viewer, node): + node_link = self.hyperlink(node.get_absolute_url(), node.headline) + + if node.parent: + node_desc = _("on %(link)s") % {'link': node_link} + else: + node_desc = node_link + + return _("%(user)s %(node_name)s %(node_desc)s") % { + 'user': self.hyperlink(node.author.get_profile_url(), self.friendly_ownername(viewer, node.author)), + 'node_name': node.friendly_name, 'node_desc': node_desc, + } + + class Meta: + proxy = True + +class DummyActionProxy(Action): + __metaclass__ = ActionProxyMetaClass + + hooks = [] + + def process_data(self, **data): + pass + + def process_action(self): + pass + + def save(self, data=None): + self.process_action() + + if data: + self.process_data(**data) + + for hook in self.__class__.hooks: + hook(self, True) + + @classmethod + def get_type(cls): + return re.sub(r'action$', '', cls.__name__.lower()) + + @classmethod + def hook(cls, fn): + cls.hooks.append(fn) + + + +class ActionRepute(models.Model): + action = models.ForeignKey(Action, related_name='reputes') + date = models.DateTimeField(default=datetime.datetime.now) + user = models.ForeignKey('User', related_name='reputes') + value = models.IntegerField(default=0) + by_canceled = models.BooleanField(default=False) + + @property + def positive(self): + if self.value > 0: return self.value + return 0 + + @property + def negative(self): + if self.value < 0: return self.value + return 0 + + def save(self, *args, **kwargs): + super(ActionRepute, self).save(*args, **kwargs) + self.user.reputation = models.F('reputation') + self.value + self.user.save() + + def delete(self): + self.user.reputation = models.F('reputation') - self.value + self.user.save() + super(ActionRepute, self).delete() + + class Meta: + app_label = 'forum' + diff --git a/forum/models/answer.py b/forum/models/answer.py index 01e0aae..090e19d 100644 --- a/forum/models/answer.py +++ b/forum/models/answer.py @@ -1,79 +1,23 @@ from base import * +from django.utils.translation import ugettext as _ -from question import Question +class Answer(Node): + friendly_name = _("answer") -class Answer(QandA): - accepted = models.BooleanField(default=False) - accepted_at = models.DateTimeField(null=True, blank=True) - accepted_by = models.ForeignKey(User, null=True) + class Meta(Node.Meta): + proxy = True - class Meta(QandA.Meta): - db_table = u'answer' + @property + def accepted(self): + return self.extra_action @property def headline(self): return self.question.headline - def mark_accepted(self, user): - if not self.accepted and not self.question.answer_accepted: - self.accepted = True - self.accepted_at = datetime.datetime.now() - self.accepted_by = user - self.save() - self.question.accepted_answer = self - self.question.save() - answer_accepted.send(sender=Answer, answer=self, user=user) - return True - - return False - - def unmark_accepted(self, user): - if self.accepted: - self.accepted = False - self.save() - self.question.accepted_answer = None - self.question.save() - answer_accepted_canceled.send(sender=Answer, answer=self, user=user) - return True - - return False - - def _update_question_answer_count(self, diff): - self.question.answer_count = self.question.answer_count + diff - self.question.save() - - def activate_revision(self, user, revision): - super(Answer, self).activate_revision(user, revision) - self.question.update_last_activity(user) - - def mark_deleted(self, user): - if super(Answer, self).mark_deleted(user): - self._update_question_answer_count(-1) - - def unmark_deleted(self): - if super(Answer, self).unmark_deleted(): - self._update_question_answer_count(1) - - def get_latest_revision(self): - return self.revisions.all()[0] - - def get_question_title(self): - return self.question.title - def get_absolute_url(self): return '%s#%s' % (self.question.get_absolute_url(), self.id) - def save(self, *args, **kwargs): - super(Answer, self).save(*args, **kwargs) - - if self._is_new: - self._update_question_answer_count(1) - - def __unicode__(self): - return self.html - -answer_accepted = django.dispatch.Signal(providing_args=['answer', 'user']) -answer_accepted_canceled = django.dispatch.Signal(providing_args=['answer', 'user']) class AnswerRevision(NodeRevision): class Meta: diff --git a/forum/models/base.py b/forum/models/base.py index 43668d9..56124a8 100644 --- a/forum/models/base.py +++ b/forum/models/base.py @@ -20,15 +20,43 @@ import logging from forum.const import * +class LazyQueryList(object): + def __init__(self, model, items): + self.model = model + self.items = items + + def __getitem__(self, k): + return self.model.objects.get(id=self.items[k]) + + def __iter__(self): + for id in self.items: + yield self.model.objects.get(id=id) + + def __len__(self): + return len(self.items) + +class CachedQuerySet(models.query.QuerySet): + def lazy(self): + if len(self.query.extra) == 0: + return LazyQueryList(self.model, list(self.values_list('id', flat=True))) + else: + return self + +from action import Action + class CachedManager(models.Manager): use_for_related_fields = True int_cache_re = re.compile('^_[\w_]+cache$') + def get_query_set(self): + return CachedQuerySet(self.model) + def cache_obj(self, obj): int_cache_keys = [k for k in obj.__dict__.keys() if self.int_cache_re.match(k)] - + d = obj.__dict__ for k in int_cache_keys: - del obj.__dict__[k] + if not isinstance(obj.__dict__[k], Action): + del obj.__dict__[k] cache.set(self.model.cache_key(obj.id), obj, 60 * 60) @@ -59,19 +87,54 @@ class CachedManager(models.Manager): except: return super(CachedManager, self).get_or_create(*args, **kwargs) -denorm_update = django.dispatch.Signal(providing_args=["instance", "field", "old", "new"]) -class DenormalizedField(models.IntegerField): - __metaclass__ = models.SubfieldBase +class DenormalizedField(object): + def __init__(self, manager, **kwargs): + self.manager = manager + self.filter = kwargs + + def setup_class(self, cls, name): + dict_name = '_%s_cache_' % name + + def getter(inst): + val = inst.__dict__.get(dict_name, None) + + if val is None: + val = getattr(inst, self.manager).filter(**self.filter).count() + inst.__dict__[dict_name] = val + inst.__class__.objects.cache_obj(inst) + + return val + + def reset_cache(inst): + inst.__dict__.pop(dict_name, None) + inst.__class__.objects.cache_obj(inst) + + cls.add_to_class(name, property(getter)) + cls.add_to_class("reset_%s_cache" % name, reset_cache) + + +class BaseMetaClass(models.Model.__metaclass__): + to_denormalize = [] + + def __new__(cls, *args, **kwargs): + new_cls = super(BaseMetaClass, cls).__new__(cls, *args, **kwargs) - def contribute_to_class(self, cls, name): - super (DenormalizedField, self).contribute_to_class(cls, name) - if not hasattr(cls, '_denormalizad_fields'): - cls._denormalizad_fields = [] + BaseMetaClass.to_denormalize.extend( + [(new_cls, name, field) for name, field in new_cls.__dict__.items() if isinstance(field, DenormalizedField)] + ) + + return new_cls + + @classmethod + def setup_denormalizes(cls): + for new_cls, name, field in BaseMetaClass.to_denormalize: + field.setup_class(new_cls, name) - cls._denormalizad_fields.append(name) class BaseModel(models.Model): + __metaclass__ = BaseMetaClass + objects = CachedManager() class Meta: @@ -92,35 +155,29 @@ class BaseModel(models.Model): if self._original_state.get(k, missing) == missing or self._original_state[k] != v]) def save(self, *args, **kwargs): - put_back = None - - if hasattr(self.__class__, '_denormalizad_fields'): - dirty = self.get_dirty_fields() - put_back = [f for f in self.__class__._denormalizad_fields if f in dirty] - - if put_back: - for n in put_back: - self.__dict__[n] = models.F(n) + (self.__dict__[n] - dirty[n]) - - super(BaseModel, self).save(*args, **kwargs) + put_back = [k for k, v in self.__dict__.items() if isinstance(v, models.expressions.ExpressionNode)] + super(BaseModel, self).save() if put_back: try: self.__dict__.update( self.__class__.objects.filter(id=self.id).values(*put_back)[0] ) - for f in put_back: - denorm_update.send(sender=self.__class__, instance=self, field=f, - old=self._original_state[f], new=self.__dict__[f]) except: - #todo: log this properly - pass + logging.error("Unable to read %s from %s" % (", ".join(put_back), self.__class__.__name__)) + self.uncache() self._original_state = dict(self.__dict__) + self.cache() + + def cache(self): self.__class__.objects.cache_obj(self) - def delete(self): + def uncache(self): cache.delete(self.cache_key(self.pk)) + + def delete(self): + self.uncache() super(BaseModel, self).delete() @@ -170,8 +227,6 @@ class UserContent(models.Model): app_label = 'forum' -marked_deleted = django.dispatch.Signal(providing_args=["instance", "deleted_by"]) - class DeletableContent(models.Model): deleted = models.BooleanField(default=False) deleted_at = models.DateTimeField(null=True, blank=True) @@ -189,7 +244,6 @@ class DeletableContent(models.Model): self.deleted_at = datetime.datetime.now() self.deleted_by = user self.save() - marked_deleted.send(sender=self.__class__, instance=self, deleted_by=user) return True else: return False @@ -223,19 +277,6 @@ class CancelableContent(models.Model): from node import Node, NodeRevision -class QandA(Node): - wiki = models.BooleanField(default=False) - wikified_at = models.DateTimeField(null=True, blank=True) - - class Meta: - abstract = True - app_label = 'forum' - - def wikify(self): - if not self.wiki: - self.wiki = True - self.wikified_at = datetime.datetime.now() - self.save() diff --git a/forum/models/comment.py b/forum/models/comment.py index a2e7425..a70d474 100644 --- a/forum/models/comment.py +++ b/forum/models/comment.py @@ -1,7 +1,10 @@ from base import * +from django.utils.translation import ugettext as _ import re class Comment(Node): + friendly_name = _("comment") + class Meta(Node.Meta): ordering = ('-added_at',) proxy = True @@ -13,7 +16,10 @@ class Comment(Node): @property def comment(self): - return self.body + if settings.FORM_ALLOW_MARKDOWN_IN_COMMENTS: + return self.as_markdown('limitedsyntax') + else: + return self.body @property def headline(self): @@ -26,21 +32,16 @@ class Comment(Node): def save(self, *args, **kwargs): super(Comment,self).save(*args, **kwargs) - if self._is_new: - self._update_parent_comment_count(1) - - try: - ping_google() - except Exception: - logging.debug('problem pinging google did you register your sitemap with google?') + if not self.id: + self.parent.reset_comment_count_cache() def mark_deleted(self, user): if super(Comment, self).mark_deleted(user): - self._update_parent_comment_count(-1) + self.parent.reset_comment_count_cache() def unmark_deleted(self): if super(Comment, self).unmark_deleted(): - self._update_parent_comment_count(1) + self.parent.reset_comment_count_cache() def is_reply_to(self, user): inreply = re.search('@\w+', self.body) diff --git a/forum/models/meta.py b/forum/models/meta.py index 86a479c..1498935 100644 --- a/forum/models/meta.py +++ b/forum/models/meta.py @@ -1,75 +1,86 @@ -from base import * - -class Vote(MetaContent, CancelableContent, UserContent): - VOTE_UP = +1 - VOTE_DOWN = -1 - VOTE_CHOICES = ( - (VOTE_UP, u'Up'), - (VOTE_DOWN, u'Down'), - ) - - vote = models.SmallIntegerField(choices=VOTE_CHOICES) - voted_at = models.DateTimeField(default=datetime.datetime.now) - - active = ActiveObjectManager() - - class Meta(MetaContent.Meta): - db_table = u'vote' - - def __unicode__(self): - return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote) - - def _update_post_vote_count(self, diff): - post = self.node.leaf - field = self.vote == 1 and 'vote_up_count' or 'vote_down_count' - post.__dict__[field] = post.__dict__[field] + diff - post.save() - - def cancel(self): - if super(Vote, self).cancel(): - self._update_post_vote_count(-1) - - def save(self, *args, **kwargs): - super(Vote, self).save(*args, **kwargs) - if self._is_new: - self._update_post_vote_count(1) - - def is_upvote(self): - return self.vote == self.VOTE_UP - - def is_downvote(self): - return self.vote == self.VOTE_DOWN - - -class FlaggedItem(MetaContent, UserContent): - flagged_at = models.DateTimeField(default=datetime.datetime.now) - reason = models.CharField(max_length=300, null=True) - canceled = models.BooleanField(default=False) - - active = ActiveObjectManager() - - class Meta(MetaContent.Meta): - db_table = u'flagged_item' - - def __unicode__(self): - return '[%s] flagged at %s' %(self.user, self.flagged_at) - - def _update_post_flag_count(self, diff): - post = self.node - post.offensive_flag_count = post.offensive_flag_count + diff - post.save() - - def save(self, *args, **kwargs): - super(FlaggedItem, self).save(*args, **kwargs) - if self._is_new: - self._update_post_flag_count(1) - - def cancel(self): - if not self.canceled: - self.canceled = True - self.save() - self._update_post_flag_count(-1) - - - - +from django.utils.translation import ugettext as _ +from base import * + +class Vote(models.Model): + user = models.ForeignKey(User, related_name="votes") + node = models.ForeignKey(Node, related_name="votes") + value = models.SmallIntegerField() + action = models.ForeignKey(Action, unique=True, related_name="vote") + voted_at = models.DateTimeField(default=datetime.datetime.now) + + class Meta: + app_label = 'forum' + unique_together = ('user', 'node') + + +class Flag(models.Model): + user = models.ForeignKey(User, related_name="flags") + node = models.ForeignKey(Node, related_name="flags") + reason = models.CharField(max_length=300) + action = models.ForeignKey(Action, unique=True, related_name="flag") + flagged_at = models.DateTimeField(default=datetime.datetime.now) + + class Meta: + app_label = 'forum' + unique_together = ('user', 'node') + +class BadgeManager(models.Manager): + use_for_related_fields = True + + def get(self, *args, **kwargs): + try: + pk = [v for (k,v) in kwargs.items() if k in ('pk', 'pk__exact', 'id', 'id__exact')][0] + except: + return super(BadgeManager, self).get(*args, **kwargs) + + from forum.badges.base import BadgesMeta + badge = BadgesMeta.by_id.get(pk, None) + if not badge: + return super(BadgeManager, self).get(*args, **kwargs) + return badge.ondb + +class Badge(models.Model): + GOLD = 1 + SILVER = 2 + BRONZE = 3 + + type = models.SmallIntegerField() + cls = models.CharField(max_length=50, null=True) + awarded_count = models.PositiveIntegerField(default=0) + + awarded_to = models.ManyToManyField(User, through='Award', related_name='badges') + + objects = BadgeManager() + + @property + def name(self): + cls = self.__dict__.get('_class', None) + return cls and cls.name or _("Unknown") + + @property + def description(self): + cls = self.__dict__.get('_class', None) + return cls and cls.description or _("No description available") + + @models.permalink + def get_absolute_url(self): + return ('badge', [], {'id': self.id, 'slug': slugify(self.name)}) + + class Meta: + app_label = 'forum' + + +class Award(models.Model): + user = models.ForeignKey(User) + badge = models.ForeignKey('Badge', related_name="awards") + node = models.ForeignKey(Node, null=True) + + awarded_at = models.DateTimeField(default=datetime.datetime.now) + + trigger = models.ForeignKey(Action, related_name="awards", null=True) + action = models.ForeignKey(Action, related_name="award", unique=True) + + + class Meta: + unique_together = ('user', 'badge', 'node') + app_label = 'forum' \ No newline at end of file diff --git a/forum/models/node.py b/forum/models/node.py index a92dee9..a7e122e 100644 --- a/forum/models/node.py +++ b/forum/models/node.py @@ -1,8 +1,9 @@ -from akismet import * +from forum.akismet import * from base import * from tag import Tag import markdown +from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe from django.utils.html import strip_tags from forum.utils.html import sanitize_html @@ -19,7 +20,10 @@ class NodeContent(models.Model): @property def html(self): - return mark_safe(sanitize_html(markdown.markdown(self.body))) + return self.as_markdown() + + def as_markdown(self, *extensions): + return mark_safe(sanitize_html(markdown.markdown(self.body, extensions=extensions))) @property def headline(self): @@ -38,14 +42,14 @@ class NodeContent(models.Model): abstract = True app_label = 'forum' -class NodeMetaClass(models.Model.__metaclass__): +class NodeMetaClass(BaseMetaClass): types = {} def __new__(cls, *args, **kwargs): new_cls = super(NodeMetaClass, cls).__new__(cls, *args, **kwargs) if not new_cls._meta.abstract and new_cls.__name__ is not 'Node': - NodeMetaClass.types[new_cls.__name__.lower()] = new_cls + NodeMetaClass.types[new_cls.get_type()] = new_cls return new_cls @@ -59,10 +63,7 @@ class NodeMetaClass(models.Model.__metaclass__): name = node_cls.__name__.lower() def children(self): - if node_cls._meta.proxy: - return node_cls.objects.filter(node_type=name, parent=self) - else: - return node_cls.objects.filter(parent=self) + return node_cls.objects.filter(parent=self) def parent(self): p = self.__dict__.get('_%s_cache' % name, None) @@ -77,35 +78,82 @@ class NodeMetaClass(models.Model.__metaclass__): Node.add_to_class(name, property(parent)) -node_create = django.dispatch.Signal(providing_args=['instance']) -node_edit = django.dispatch.Signal(providing_args=['instance']) +class NodeManager(CachedManager): + use_for_related_fields = True -class Node(BaseModel, NodeContent, DeletableContent): - __metaclass__ = NodeMetaClass + def get_query_set(self): + qs = super(NodeManager, self).get_query_set() + + if self.model is not Node: + return qs.filter(node_type=self.model.get_type()) + else: + return qs + + def get(self, *args, **kwargs): + node = super(NodeManager, self).get(*args, **kwargs) + cls = NodeMetaClass.types.get(node.node_type, None) + + if cls and node.__class__ is not cls: + return node.leaf + return node + + def get_for_types(self, types, *args, **kwargs): + kwargs['node_type__in'] = [t.get_type() for t in types] + return self.get(*args, **kwargs) - node_type = models.CharField(max_length=16, default='node') - parent = models.ForeignKey('Node', related_name='children', null=True) - abs_parent = models.ForeignKey('Node', related_name='all_children', null=True) - added_at = models.DateTimeField(default=datetime.datetime.now) +class Node(BaseModel, NodeContent): + __metaclass__ = NodeMetaClass - tags = models.ManyToManyField('Tag', related_name='%(class)ss') + node_type = models.CharField(max_length=16, default='node') + parent = models.ForeignKey('Node', related_name='children', null=True) + abs_parent = models.ForeignKey('Node', related_name='all_children', null=True) - score = DenormalizedField(default=0) - vote_up_count = DenormalizedField(default=0) - vote_down_count = models.IntegerField(default=0) + added_at = models.DateTimeField(default=datetime.datetime.now) + score = models.IntegerField(default=0) - comment_count = DenormalizedField(default=0) - offensive_flag_count = DenormalizedField(default=0) + deleted = models.ForeignKey('Action', null=True, unique=True, related_name="deleted_node") + in_moderation = models.ForeignKey('Action', null=True, unique=True, related_name="moderated_node") + last_edited = models.ForeignKey('Action', null=True, unique=True, related_name="edited_node") - last_edited_at = models.DateTimeField(null=True, blank=True) - last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss') + last_activity_by = models.ForeignKey(User, null=True) + last_activity_at = models.DateTimeField(null=True, blank=True) + tags = models.ManyToManyField('Tag', related_name='%(class)ss') active_revision = models.OneToOneField('NodeRevision', related_name='active', null=True) + extra_ref = models.ForeignKey('Node', null=True) + extra_count = models.IntegerField(default=0) + extra_action = models.ForeignKey('Action', null=True, related_name="extra_node") + + marked = models.BooleanField(default=False) + wiki = models.BooleanField(default=False) + + comment_count = DenormalizedField("children", node_type="comment", canceled=False) + flag_count = DenormalizedField("flags") + + friendly_name = _("post") + + objects = NodeManager() + + @classmethod + def cache_key(cls, pk): + return '%s.node:%s' % (settings.APP_URL, pk) + + @classmethod + def get_type(cls): + return cls.__name__.lower() + @property def leaf(self): - return NodeMetaClass.types[self.node_type].objects.get(id=self.id) + leaf_cls = NodeMetaClass.types.get(self.node_type, None) + + if leaf_cls is None: + return self + + leaf = leaf_cls() + leaf.__dict__ = self.__dict__ + return leaf @property def absolute_parent(self): @@ -118,40 +166,40 @@ class Node(BaseModel, NodeContent, DeletableContent): def summary(self): return strip_tags(self.html)[:300] - def create_revision(self, user, **kwargs): - revision = NodeRevision(author=user, **kwargs) - - if not self.id: - self.author = user - self.save() - revision.revision = 1 - else: - revision.revision = self.revisions.aggregate(last=models.Max('revision'))['last'] + 1 + def update_last_activity(self, user): + self.last_activity_by = user + self.last_activity_at = datetime.datetime.now() - revision.node_id = self.id + if self.parent: + self.parent.update_last_activity(user) + + def _create_revision(self, user, number, **kwargs): + revision = NodeRevision(author=user, revision=number, node=self, **kwargs) revision.save() - self.activate_revision(user, revision) + return revision + + def create_revision(self, user, action=None, **kwargs): + number = self.revisions.aggregate(last=models.Max('revision'))['last'] + 1 + revision = self._create_revision(user, number, **kwargs) + self.activate_revision(user, revision, action) + return revision - def activate_revision(self, user, revision): + def activate_revision(self, user, revision, action=None): self.title = revision.title self.tagnames = revision.tagnames self.body = revision.body - old_revision = self.active_revision self.active_revision = revision + self.update_last_activity(user) - if not old_revision: - signal = node_create - else: - self.last_edited_at = datetime.datetime.now() - self.last_edited_by = user - signal = node_edit + if action: + self.last_edited = action self.save() - signal.send(sender=self.__class__, instance=self) def get_tag_list_if_changed(self): dirty = self.get_dirty_fields() + active_user = self.last_edited and self.last_edited.by or self.author if 'tagnames' in dirty: new_tags = self.tagname_list() @@ -168,7 +216,7 @@ class Node(BaseModel, NodeContent, DeletableContent): try: tag = Tag.objects.get(name=name) except: - tag = Tag.objects.create(name=name, created_by=self.last_edited_by or self.author) + tag = Tag.objects.create(name=name, created_by=active_user or self.author) tag_list.append(tag) @@ -182,7 +230,7 @@ class Node(BaseModel, NodeContent, DeletableContent): tag = Tag.objects.get(name=name) tag.used_count = tag.used_count - 1 if tag.used_count == 0: - tag.mark_deleted(self.last_edited_by or self.author) + tag.mark_deleted(active_user) tag.save() return tag_list @@ -191,13 +239,14 @@ class Node(BaseModel, NodeContent, DeletableContent): def save(self, *args, **kwargs): if not self.id: - self.node_type = self.__class__.__name__.lower() + self.node_type = self.get_type() + super(BaseModel, self).save(*args, **kwargs) + self.active_revision = self._create_revision(self.author, 1, title=self.title, tagnames=self.tagnames, body=self.body) + self.update_last_activity(self.author) if self.parent_id and not self.abs_parent_id: self.abs_parent = self.parent.absolute_parent - - self.__dict__['score'] = self.__dict__['vote_up_count'] - self.__dict__['vote_down_count'] - + tags = self.get_tag_list_if_changed() super(Node, self).save(*args, **kwargs) if tags is not None: self.tags = tags @@ -205,8 +254,9 @@ class Node(BaseModel, NodeContent, DeletableContent): @staticmethod def isSpam(comment, data): api = Akismet() - if api.key is None: - print "problem" # raise APIKeyError + + if not api.key: + return False else: if api.comment_check(comment, data): return True @@ -229,11 +279,3 @@ class NodeRevision(BaseModel, NodeContent): app_label = 'forum' -from user import ValidationHash - -class AnonymousNode(Node): - validation_hash = models.ForeignKey(Node, related_name='anonymous_content') - convertible_to = models.CharField(max_length=16, default='node') - - class Meta: - app_label = 'forum' \ No newline at end of file diff --git a/forum/models/question.py b/forum/models/question.py index da8083f..5484016 100644 --- a/forum/models/question.py +++ b/forum/models/question.py @@ -4,29 +4,26 @@ from django.utils.translation import ugettext as _ question_view = django.dispatch.Signal(providing_args=['instance', 'user']) -class Question(QandA): - accepted_answer = models.OneToOneField('Answer', null=True, related_name="question_accepting") - closed = models.BooleanField(default=False) - closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions') - closed_at = models.DateTimeField(null=True, blank=True) - close_reason = models.SmallIntegerField(choices=CLOSE_REASONS, null=True, blank=True) - subscribers = models.ManyToManyField(User, related_name='subscriptions', through='QuestionSubscription') - - # Denormalised data - answer_count = models.PositiveIntegerField(default=0) - view_count = models.IntegerField(default=0) - favourite_count = models.IntegerField(default=0) - last_activity_at = models.DateTimeField(default=datetime.datetime.now) - last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions', null=True) - - favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions') - - class Meta(QandA.Meta): - db_table = u'question' +class Question(Node): + class Meta(Node.Meta): + proxy = True + + answer_count = DenormalizedField("children", node_type="answer", deleted=None) + favorite_count = DenormalizedField("actions", action_type="favorite", canceled=False) + + friendly_name = _("question") + + @property + def closed(self): + return self.extra_action + + @property + def view_count(self): + return self.extra_count @property def headline(self): - if self.closed: + if self.marked: return _('[closed] ') + self.title if self.deleted: @@ -36,43 +33,16 @@ class Question(QandA): @property def answer_accepted(self): - return self.accepted_answer is not None + return self.extra_ref is not None - def save(self, *args, **kwargs): - if not self.last_activity_by: - self.last_activity_by = self.author - super(Question, self).save(*args, **kwargs) - - def update_last_activity(self, user): - self.last_activity_by = user - self.last_activity_at = datetime.datetime.now() - self.save() - - def activate_revision(self, user, revision): - super(Question, self).activate_revision(user, revision) - self.update_last_activity(user) + @property + def accepted_answer(self): + return self.extra_ref @models.permalink def get_absolute_url(self): return ('question', (), {'id': self.id, 'slug': django_urlquote(slugify(self.title))}) - def get_answer_count_by_user(self, user_id): - from answer import Answer - query_set = Answer.objects.filter(author__id=user_id) - return query_set.filter(question=self).count() - - def get_question_title(self): - if self.closed: - attr = CONST['closed'] - elif self.deleted: - attr = CONST['deleted'] - else: - attr = None - if attr is not None: - return u'%s %s' % (self.title, attr) - else: - return self.title - def get_revision_url(self): return reverse('question_revisions', args=[self.id]) @@ -82,49 +52,22 @@ class Question(QandA): if related_list is None: related_list = Question.objects.values('id').filter(tags__id__in=[t.id for t in self.tags.all()] - ).exclude(id=self.id).exclude(deleted=True).annotate(frequency=models.Count('id')).order_by('-frequency')[:count] + ).exclude(id=self.id).filter(deleted=None).annotate(frequency=models.Count('id')).order_by('-frequency')[:count] cache.set(cache_key, related_list, 60 * 60) return [Question.objects.get(id=r['id']) for r in related_list] - def __unicode__(self): - return self.title def question_viewed(instance, **kwargs): - instance.view_count += 1 + instance.extra_count += 1 instance.save() question_view.connect(question_viewed) -class FavoriteQuestion(models.Model): - question = models.ForeignKey('Question') - user = models.ForeignKey(User, related_name='user_favorite_questions') - added_at = models.DateTimeField(default=datetime.datetime.now) - - class Meta: - unique_together = ('question', 'user') - app_label = 'forum' - db_table = u'favorite_question' - - def __unicode__(self): - return '[%s] favorited at %s' %(self.user, self.added_at) - - def _update_question_fav_count(self, diff): - self.question.favourite_count = self.question.favourite_count + diff - self.question.save() - - def save(self, *args, **kwargs): - super(FavoriteQuestion, self).save(*args, **kwargs) - if self._is_new: - self._update_question_fav_count(1) - - def delete(self): - self._update_question_fav_count(-1) - super(FavoriteQuestion, self).delete() class QuestionSubscription(models.Model): user = models.ForeignKey(User) - question = models.ForeignKey(Question) + question = models.ForeignKey(Node) auto_subscription = models.BooleanField(default=True) last_view = models.DateTimeField(default=datetime.datetime.now()) diff --git a/forum/models/repute.py b/forum/models/repute.py deleted file mode 100644 index 0c54c1b..0000000 --- a/forum/models/repute.py +++ /dev/null @@ -1,127 +0,0 @@ -from base import * -from django.contrib.contenttypes.models import ContentType -from forum.models import User - -from django.utils.translation import ugettext as _ - -class Badge(BaseModel): - """Awarded for notable actions performed on the site by Users.""" - GOLD = 1 - SILVER = 2 - BRONZE = 3 - TYPE_CHOICES = ( - (GOLD, _('gold')), - (SILVER, _('silver')), - (BRONZE, _('bronze')), - ) - - name = models.CharField(max_length=50) - type = models.SmallIntegerField(choices=TYPE_CHOICES) - slug = models.SlugField(max_length=50, blank=True) - description = models.CharField(max_length=300) - multiple = models.BooleanField(default=False) - # Denormalised data - awarded_count = models.PositiveIntegerField(default=0) - awarded_to = models.ManyToManyField(User, through='Award', related_name='badges') - - class Meta: - app_label = 'forum' - db_table = u'badge' - ordering = ('name',) - unique_together = ('name', 'type') - - def __unicode__(self): - return u'%s: %s' % (self.get_type_display(), self.name) - - def save(self, *args, **kwargs): - if not self.slug: - self.slug = self.name#slugify(self.name) - super(Badge, self).save(*args, **kwargs) - - def get_absolute_url(self): - return '%s%s/' % (reverse('badge', args=[self.id]), self.slug) - - -class AwardManager(models.Manager): - def get_recent_awards(self): - awards = super(AwardManager, self).extra( - select={'badge_id': 'badge.id', 'badge_name':'badge.name', - 'badge_description': 'badge.description', 'badge_type': 'badge.type', - 'user_id': 'auth_user.id', 'user_name': 'auth_user.username' - }, - tables=['award', 'badge', 'auth_user'], - order_by=['-awarded_at'], - where=['auth_user.id=award.user_id AND badge_id=badge.id'], - ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name') - return awards - -class Award(GenericContent, UserContent): - """The awarding of a Badge to a User.""" - badge = models.ForeignKey('Badge', related_name='award_badge') - awarded_at = models.DateTimeField(default=datetime.datetime.now) - notified = models.BooleanField(default=False) - - objects = AwardManager() - - def __unicode__(self): - return u'[%s] is awarded a badge [%s] at %s' % (self.user.username, self.badge.name, self.awarded_at) - - def save(self, *args, **kwargs): - super(Award, self).save(*args, **kwargs) - - if self._is_new: - self.badge.awarded_count += 1 - self.badge.save() - - if self.badge.type == Badge.GOLD: - self.user.gold += 1 - if self.badge.type == Badge.SILVER: - self.user.silver += 1 - if self.badge.type == Badge.BRONZE: - self.user.bronze += 1 - self.user.save() - - class Meta: - #unique_together = ('content_type', 'object_id', 'user', 'badge') - app_label = 'forum' - db_table = u'award' - - -class Repute(MetaContent, CancelableContent, UserContent): - value = models.SmallIntegerField(default=0) - question = models.ForeignKey('Question') - reputed_at = models.DateTimeField(default=datetime.datetime.now) - reputation_type = models.SmallIntegerField(choices=TYPE_REPUTATION) - user_previous_rep = models.IntegerField(default=0) - - def __unicode__(self): - return u'[%s]\' reputation changed at %s' % (self.user.username, self.reputed_at) - - @property - def positive(self): - if self.value > 0: return self.value - return 0 - - @property - def negative(self): - if self.value < 0: return self.value - return 0 - - @property - def reputation(self): - return self.user_previous_rep + self.value - - def cancel(self): - if super(Repute, self).cancel(): - self.user.reputation = self.user.reputation - self.value - self.user.save() - - def save(self, *args, **kwargs): - self.user_previous_rep = self.user.reputation - self.user.reputation = self.user.reputation + self.value - self.user.save() - super(Repute, self).save(*args, **kwargs) - - class Meta: - app_label = 'forum' - db_table = u'repute' diff --git a/forum/models/tag.py b/forum/models/tag.py index 50eb5aa..16f1c6c 100644 --- a/forum/models/tag.py +++ b/forum/models/tag.py @@ -18,7 +18,6 @@ class Tag(BaseModel, DeletableContent): active = ActiveTagManager() class Meta(DeletableContent.Meta): - db_table = u'tag' ordering = ('-used_count', 'name') def __unicode__(self): diff --git a/forum/models/user.py b/forum/models/user.py index abb3989..53b5ee2 100644 --- a/forum/models/user.py +++ b/forum/models/user.py @@ -6,7 +6,8 @@ from django.db.models import Q try: from hashlib import md5 except: - import md5 + from md5 import new as md5 + import string from random import Random @@ -26,7 +27,7 @@ class UserManager(CachedManager): class AnonymousUser(DjangoAnonymousUser): def get_visible_answers(self, question): - return question.answers.filter(deleted=False) + return question.answers.filter(deleted=None) def can_view_deleted_post(self, post): return False @@ -76,18 +77,19 @@ class AnonymousUser(DjangoAnonymousUser): def can_upload_files(self): return False +def true_if_is_super_or_staff(fn): + def decorated(self, *args, **kwargs): + return self.is_superuser or self.is_staff or fn(self, *args, **kwargs) + return decorated + class User(BaseModel, DjangoUser): is_approved = models.BooleanField(default=False) email_isvalid = models.BooleanField(default=False) - email_key = models.CharField(max_length=32, null=True) - reputation = DenormalizedField(default=1) - gold = DenormalizedField(default=0) - silver = DenormalizedField(default=0) - bronze = DenormalizedField(default=0) - - questions_per_page = models.SmallIntegerField(choices=QUESTIONS_PER_PAGE_CHOICES, default=10) - hide_ignored_questions = models.BooleanField(default=False) + reputation = models.PositiveIntegerField(default=0) + gold = models.PositiveIntegerField(default=0) + silver = models.PositiveIntegerField(default=0) + bronze = models.PositiveIntegerField(default=0) last_seen = models.DateTimeField(default=datetime.datetime.now) real_name = models.CharField(max_length=100, blank=True) @@ -95,7 +97,12 @@ class User(BaseModel, DjangoUser): location = models.CharField(max_length=100, blank=True) date_of_birth = models.DateField(null=True, blank=True) about = models.TextField(blank=True) - + + subscriptions = models.ManyToManyField('Node', related_name='subscribers', through='QuestionSubscription') + + vote_up_count = DenormalizedField("actions", canceled=False, action_type="voteup") + vote_down_count = DenormalizedField("actions", canceled=False, action_type="votedown") + objects = UserManager() @property @@ -104,10 +111,16 @@ class User(BaseModel, DjangoUser): def save(self, *args, **kwargs): if self.reputation < 0: - self.reputation = 1 + self.reputation = 0 + + new = not bool(self.id) super(User, self).save(*args, **kwargs) + if new: + sub_settings = SubscriptionSettings(user=self) + sub_settings.save() + def get_absolute_url(self): return self.get_profile_url() @@ -127,15 +140,13 @@ class User(BaseModel, DjangoUser): profile_link = u'%s' % (self.get_profile_url(),self.username) return mark_safe(profile_link) + def get_visible_answers(self, question): + return question.answers.filter(deleted=None, in_moderation=None) + def get_vote_count_today(self): today = datetime.date.today() - return self.votes.filter(voted_at__range=(today - datetime.timedelta(days=1), today)).count() - - def get_up_vote_count(self): - return self.votes.filter(vote=1).count() - - def get_down_vote_count(self): - return self.votes.filter(vote=-1).count() + return self.actions.filter(canceled=False, action_type__in=("voteup", "votedown"), + action_date__range=(today - datetime.timedelta(days=1), today)).count() def get_reputation_by_upvoted_today(self): today = datetime.datetime.now() @@ -149,130 +160,87 @@ class User(BaseModel, DjangoUser): def get_flagged_items_count_today(self): today = datetime.date.today() - return self.flaggeditems.filter(flagged_at__range=(today - datetime.timedelta(days=1), today)).count() - - def get_visible_answers(self, question): - if self.is_superuser: - return question.answers - else: - return question.answers.filter(models.Q(deleted=False) | models.Q(deleted_by=self)) + return self.actions.filter(canceled=False, action_type="flag", + action_date__range=(today - datetime.timedelta(days=1), today)).count() + @true_if_is_super_or_staff def can_view_deleted_post(self, post): - return self.is_superuser or post.author == self + return post.author == self + @true_if_is_super_or_staff def can_vote_up(self): - return self.reputation >= int(settings.REP_TO_VOTE_UP) or self.is_superuser + return self.reputation >= int(settings.REP_TO_VOTE_UP) + @true_if_is_super_or_staff def can_vote_down(self): - return self.reputation >= int(settings.REP_TO_VOTE_DOWN) or self.is_superuser + return self.reputation >= int(settings.REP_TO_VOTE_DOWN) def can_flag_offensive(self, post=None): if post is not None and post.author == self: return False - return self.is_superuser or self.reputation >= int(settings.REP_TO_FLAG) + return self.is_superuser or self.is_staff or self.reputation >= int(settings.REP_TO_FLAG) + @true_if_is_super_or_staff def can_view_offensive_flags(self, post=None): if post is not None and post.author == self: return True - return self.is_superuser or self.reputation >= int(settings.REP_TO_VIEW_FLAGS) + return self.reputation >= int(settings.REP_TO_VIEW_FLAGS) + @true_if_is_super_or_staff def can_comment(self, post): return self == post.author or self.reputation >= int(settings.REP_TO_COMMENT - ) or self.is_superuser or (post.__class__.__name__ == "Answer" and self == post.question.author) + ) or (post.__class__.__name__ == "Answer" and self == post.question.author) + @true_if_is_super_or_staff def can_like_comment(self, comment): - return self != comment.user and (self.reputation >= int(settings.REP_TO_LIKE_COMMENT) or self.is_superuser) + return self != comment.author and (self.reputation >= int(settings.REP_TO_LIKE_COMMENT)) + @true_if_is_super_or_staff def can_edit_comment(self, comment): - return (comment.user == self and comment.added_at >= datetime.datetime.now() - datetime.timedelta(minutes=60) + return (comment.author == self and comment.added_at >= datetime.datetime.now() - datetime.timedelta(minutes=60) ) or self.is_superuser + @true_if_is_super_or_staff def can_delete_comment(self, comment): - return self == comment.user or self.reputation >= int(settings.REP_TO_DELETE_COMMENTS) or self.is_superuser + return self == comment.author or self.reputation >= int(settings.REP_TO_DELETE_COMMENTS) + @true_if_is_super_or_staff def can_accept_answer(self, answer): - return self.is_superuser or self == answer.question.author + return self == answer.question.author + @true_if_is_super_or_staff def can_edit_post(self, post): - return self.is_superuser or self == post.author or self.reputation >= int(settings.REP_TO_EDIT_OTHERS + return self == post.author or self.reputation >= int(settings.REP_TO_EDIT_OTHERS ) or (post.wiki and self.reputation >= int(settings.REP_TO_EDIT_WIKI)) + @true_if_is_super_or_staff def can_retag_questions(self): return self.reputation >= int(settings.REP_TO_RETAG) + @true_if_is_super_or_staff def can_close_question(self, question): - return self.is_superuser or (self == question.author and self.reputation >= int(settings.REP_TO_CLOSE_OWN) + return (self == question.author and self.reputation >= int(settings.REP_TO_CLOSE_OWN) ) or self.reputation >= int(settings.REP_TO_CLOSE_OTHERS) + @true_if_is_super_or_staff def can_reopen_question(self, question): - return self.is_superuser or (self == question.author and self.reputation >= settings.REP_TO_REOPEN_OWN) + return self == question.author and self.reputation >= settings.REP_TO_REOPEN_OWN + @true_if_is_super_or_staff def can_delete_post(self, post): - return self.is_superuser or (self == post.author and (post.__class__.__name__ == "Answer" or - not post.answers.filter(~Q(author=self)).count())) + if post.node_type == "comment": + return self.can_delete_comment(post) + + return (self == post.author and (post.__class__.__name__ == "Answer" or + not post.answers.exclude(author=self).count())) + @true_if_is_super_or_staff def can_upload_files(self): - return self.is_superuser or self.reputation >= int(settings.REP_TO_UPLOAD) + return self.reputation >= int(settings.REP_TO_UPLOAD) class Meta: app_label = 'forum' -class Activity(GenericContent): - """ - We keep some history data for user activities - """ - user = models.ForeignKey(User) - activity_type = models.SmallIntegerField(choices=TYPE_ACTIVITY) - active_at = models.DateTimeField(default=datetime.datetime.now) - is_auditted = models.BooleanField(default=False) - - class Meta: - app_label = 'forum' - db_table = u'activity' - - def __unicode__(self): - return u'[%s] was active at %s' % (self.user.username, self.active_at) - - def save(self, *args, **kwargs): - super(Activity, self).save(*args, **kwargs) - if self._is_new: - activity_record.send(sender=self.activity_type, instance=self) - - @property - def node(self): - if self.activity_type in (const.TYPE_ACTIVITY_ANSWER, const.TYPE_ACTIVITY_ASK_QUESTION, - const.TYPE_ACTIVITY_MARK_ANSWER, const.TYPE_ACTIVITY_COMMENT_QUESTION, const.TYPE_ACTIVITY_COMMENT_ANSWER): - return self.content_object.leaf - - if self.activity_type in (const.TYPE_ACTIVITY_UPDATE_ANSWER, const.TYPE_ACTIVITY_UPDATE_QUESTION): - return self.content_object.node.leaf - - raise NotImplementedError() - - @property - def type_as_string(self): - if self.activity_type == const.TYPE_ACTIVITY_ASK_QUESTION: - return _("asked") - elif self.activity_type == const.TYPE_ACTIVITY_ANSWER: - return _("answered") - elif self.activity_type == const.TYPE_ACTIVITY_MARK_ANSWER: - return _("marked an answer") - elif self.activity_type == const.TYPE_ACTIVITY_UPDATE_QUESTION: - return _("edited a question") - elif self.activity_type == const.TYPE_ACTIVITY_COMMENT_QUESTION: - return _("commented a question") - elif self.activity_type == const.TYPE_ACTIVITY_COMMENT_ANSWER: - return _("commented an answer") - elif self.activity_type == const.TYPE_ACTIVITY_UPDATE_ANSWER: - return _("edited an answer") - elif self.activity_type == const.TYPE_ACTIVITY_PRIZE: - return _("received badge") - else: - raise NotImplementedError() - - -activity_record = django.dispatch.Signal(providing_args=['instance']) - class SubscriptionSettings(models.Model): user = models.OneToOneField(User, related_name='subscription_settings') diff --git a/forum/models/utils.py b/forum/models/utils.py index 984a55c..5ac8900 100644 --- a/forum/models/utils.py +++ b/forum/models/utils.py @@ -1,44 +1,84 @@ from django.db import models from django.core.cache import cache from django.conf import settings -#try: -# import cPickle as pickle -#except ImportError: -import pickle -import django.dispatch +from django.utils.encoding import force_unicode + +try: + from cPickle import loads, dumps +except ImportError: + from pickle import loads, dumps + +from copy import deepcopy +from base64 import b64encode, b64decode +from zlib import compress, decompress + class PickledObject(str): - pass + pass + +def dbsafe_encode(value, compress_object=True): + if not compress_object: + value = b64encode(dumps(deepcopy(value))) + else: + value = b64encode(compress(dumps(deepcopy(value)))) + return PickledObject(value) + +def dbsafe_decode(value, compress_object=True): + if not compress_object: + value = loads(b64decode(value)) + else: + value = loads(decompress(b64decode(value))) + return value class PickledObjectField(models.Field): __metaclass__ = models.SubfieldBase + def __init__(self, *args, **kwargs): + self.compress = kwargs.pop('compress', True) + self.protocol = kwargs.pop('protocol', 2) + kwargs.setdefault('null', True) + kwargs.setdefault('editable', False) + super(PickledObjectField, self).__init__(*args, **kwargs) + + def get_default(self): + if self.has_default(): + if callable(self.default): + return self.default() + return self.default + + return super(PickledObjectField, self).get_default() + def to_python(self, value): - if isinstance (value, PickledObject): - return value - - try: - return pickle.loads(value.encode('utf-8')) - except: - return value - - def get_db_prep_save(self, value): if value is not None: - if isinstance(value, PickledObject): - return str(value) - else: - value = pickle.dumps(value) + try: + value = dbsafe_decode(value, self.compress) + except: + if isinstance(value, PickledObject): + raise + return value + def get_db_prep_value(self, value): + if value is not None and not isinstance(value, PickledObject): + value = force_unicode(dbsafe_encode(value, self.compress)) return value + def value_to_string(self, obj): + value = self._get_val_from_obj(obj) + return self.get_db_prep_value(value) + def get_internal_type(self): return 'TextField' + def get_db_prep_lookup(self, lookup_type, value): + if lookup_type not in ['exact', 'in', 'isnull']: + raise TypeError('Lookup type %s is not supported.' % lookup_type) + return super(PickledObjectField, self).get_db_prep_lookup(lookup_type, value) + class KeyValueManager(models.Manager): def create_cache_key(self, key): - return "%s:key_value:%s" % (settings.APP_URL, key) + return "%s:keyvalue:%s" % (settings.APP_URL, key) def save_to_cache(self, instance): cache.set(self.create_cache_key(instance.key), instance, 2592000) diff --git a/forum/modules.py b/forum/modules/__init__.py similarity index 97% rename from forum/modules.py rename to forum/modules/__init__.py index 10bbf65..023b464 100644 --- a/forum/modules.py +++ b/forum/modules/__init__.py @@ -7,7 +7,7 @@ from django.conf import settings MODULES_PACKAGE = 'forum_modules' -MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../' + MODULES_PACKAGE) +MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../../' + MODULES_PACKAGE) DISABLED_MODULES = getattr(settings, 'DISABLED_MODULES', []) diff --git a/forum/modules/decorators.py b/forum/modules/decorators.py new file mode 100644 index 0000000..17d248b --- /dev/null +++ b/forum/modules/decorators.py @@ -0,0 +1,53 @@ +import inspect + +class DecoratableObject(object): + def __init__(self, fn): + self._callable = fn + + def decorate(self, fn, needs_origin): + origin = self._callable + + if needs_origin: + self._callable = lambda *args, **kwargs: fn(origin, *args, **kwargs) + else: + self._callable = lambda *args, **kwargs: fn(*args, **kwargs) + + def __call__(self, *args, **kwargs): + return self._callable(*args, **kwargs) + + +def decoratable(fn): + return DecoratableObject(fn) + +def decoratable_method(fn): + obj = DecoratableObject(fn) + def decorated(self, *args, **kwargs): + return obj(self, *args, **kwargs) + + decorated.__obj = obj + return decorated + +decoratable.method = decoratable_method + +def decorate(origin, needs_origin=True): + if not isinstance(origin, DecoratableObject): + if hasattr(origin, '__obj'): + def decorator(fn): + origin.__obj.decorate(fn, needs_origin) + return origin + return decorator + + raise Exception('Not an decoratable function: %s' % origin.name) + + def decorator(fn): + origin.decorate(fn, needs_origin) + return origin + + return decorator + + + + + + + diff --git a/forum/reputation.py b/forum/reputation.py deleted file mode 100644 index 71f9d65..0000000 --- a/forum/reputation.py +++ /dev/null @@ -1,101 +0,0 @@ -from django.db.models.signals import post_save -from forum.models.base import mark_canceled -from forum.models.answer import answer_accepted, answer_accepted_canceled - -from forum.models import * -from forum.const import * -import settings - -def on_flagged_item(instance, created, **kwargs): - if not created: - return - - post = instance.content_object.leaf - question = (post.__class__ == Question) and post or post.question - - post.author.reputes.create(value=-int(settings.REP_LOST_BY_FLAGGED), question=question, - reputation_type=TYPE_REPUTATION_LOST_BY_FLAGGED) - - - if post.offensive_flag_count == settings.FLAG_COUNT_TO_HIDE_POST: - post.author.reputes.create(value=-int(settings.REP_LOST_BY_FLAGGED_3_TIMES), - question=question, reputation_type=TYPE_REPUTATION_LOST_BY_FLAGGED_3_TIMES) - - if post.offensive_flag_count == settings.FLAG_COUNT_TO_DELETE_POST: - post.author.reputes.create(value=-int(settings.REP_LOST_BY_FLAGGED_5_TIMES), - question=question, reputation_type=TYPE_REPUTATION_LOST_BY_FLAGGED_5_TIMES) - - post.mark_deleted(User.objects.get_site_owner()) - -post_save.connect(on_flagged_item, sender=FlaggedItem) - -def on_answer_accepted(answer, user, **kwargs): - if user == answer.question.author and not user == answer.author: - user.reputes.create( - value=int(settings.REP_GAIN_BY_ACCEPTING), question=answer.question, - reputation_type=TYPE_REPUTATION_GAIN_BY_ACCEPTING_ANSWER) - - if not user == answer.author: - answer.author.reputes.create( - value=int(settings.REP_GAIN_BY_ACCEPTED), question=answer.question, - reputation_type=TYPE_REPUTATION_GAIN_BY_ANSWER_ACCEPTED) - -answer_accepted.connect(on_answer_accepted) - - -def on_answer_accepted_canceled(answer, user, **kwargs): - if user == answer.accepted_by: - user.reputes.create( - value=-int(settings.REP_LOST_BY_CANCELING_ACCEPTED), question=answer.question, - reputation_type=TYPE_REPUTATION_LOST_BY_CANCELLING_ACCEPTED_ANSWER) - - if not user == answer.author: - answer.author.reputes.create( - value=-int(settings.REP_LOST_BY_ACCEPTED_CANCELED), question=answer.question, - reputation_type=TYPE_REPUTATION_LOST_BY_ACCEPTED_ANSWER_CANCELED) - -answer_accepted_canceled.connect(on_answer_accepted) - - -def on_vote(instance, created, **kwargs): - if created and (instance.content_object.node_type in ("question", "answer") and not instance.content_object.wiki): - post = instance.content_object.leaf - question = (post.__class__ == Question) and post or post.question - - if instance.vote == -1: - instance.user.reputes.create(value=-int(settings.REP_LOST_BY_DOWNVOTING), - question=question, reputation_type=TYPE_REPUTATION_LOST_BY_DOWNVOTING) - - if instance.vote == 1 and post.author.get_reputation_by_upvoted_today() >= int(settings.MAX_REP_BY_UPVOTE_DAY): - return - - repute_type, repute_value = (instance.vote == 1) and ( - TYPE_REPUTATION_GAIN_BY_UPVOTED, int(settings.REP_GAIN_BY_UPVOTED)) or ( - TYPE_REPUTATION_LOST_BY_DOWNVOTED, -int(settings.REP_LOST_BY_DOWNVOTED)) - - post.author.reputes.create(value=repute_value, question=question, reputation_type=repute_type) - -post_save.connect(on_vote, sender=Vote) - - -def on_vote_canceled(instance, **kwargs): - if instance.content_object.node_type in ("question", "answer") and not instance.content_object.wiki: - post = instance.content_object.leaf - question = (post.__class__ == Question) and post or post.question - - if instance.vote == -1: - instance.user.reputes.create(value=int(settings.REP_GAIN_BY_CANCELING_DOWNVOTE), - question=question, reputation_type=TYPE_REPUTATION_GAIN_BY_CANCELING_DOWNVOTE) - - repute_type, repute_value = (instance.vote == 1) and ( - TYPE_REPUTATION_LOST_BY_UPVOTE_CANCELED, -int(settings.REP_LOST_BY_UPVOTE_CANCELED)) or ( - TYPE_REPUTATION_GAIN_BY_DOWNVOTE_CANCELED, int(settings.REP_GAIN_BY_DOWNVOTE_CANCELED)) - - post.author.reputes.create(value=repute_value, question=question, reputation_type=repute_type) - -mark_canceled.connect(on_vote_canceled, sender=Vote) - - - - - diff --git a/forum/settings/__init__.py b/forum/settings/__init__.py index 58ed460..a8530ee 100644 --- a/forum/settings/__init__.py +++ b/forum/settings/__init__.py @@ -18,6 +18,7 @@ from upload import * from about import * from faq import * from form import * +from moderation import * BADGES_SET = SettingSet('badges', _('Badges config'), _("Configure badges on your OSQA site."), 500) diff --git a/forum/settings/base.py b/forum/settings/base.py index dc01577..5bcf5fc 100644 --- a/forum/settings/base.py +++ b/forum/settings/base.py @@ -8,12 +8,32 @@ class SettingSet(list): self.description = description self.weight = weight self.markdown = markdown + class BaseSetting(object): - def __init__(self, name, default, field_context): + @classmethod + def add_to_class(cls, name): + def wrapper(self, *args, **kwargs): + return self.value.__getattribute__(name)(*args, **kwargs) + + setattr(cls, name, wrapper) + + def __init__(self, name, default, set=None, field_context=None): self.name = name self.default = default - self.field_context = field_context + self.field_context = field_context or {} + + if set is not None: + if not set.name in Setting.sets: + Setting.sets[set.name] = set + + Setting.sets[set.name].append(self) + + def __str__(self): + return str(self.value) + + def __unicode__(self): + return unicode(self.value) @property def value(self): @@ -29,112 +49,44 @@ class BaseSetting(object): def set_value(self, new_value): new_value = self._parse(new_value) + self.save(new_value) + + def save(self, value): from forum.models import KeyValue try: kv = KeyValue.objects.get(key=self.name) - old_value = kv.value except: kv = KeyValue(key=self.name) - old_value = self.default - kv.value = new_value + kv.value = value kv.save() - setting_update.send(sender=self, old_value=old_value, new_value=new_value) - def to_default(self): self.set_value(self.default) def _parse(self, value): return value - def __str__(self): - return str(self.value) - - def __unicode__(self): - return unicode(self.value) - - def __nonzero__(self): - return bool(self.value) - - -class StringSetting(BaseSetting): - def _parse(self, value): - if isinstance(value, unicode): - return value.encode('utf8') - else: - return str(value) - - def __unicode__(self): - return unicode(self.value.decode('utf8')) - - def __add__(self, other): - return "%s%s" % (unicode(self), other) - - def __cmp__(self, other): - return cmp(str(self), str(other)) - -class IntegerSetting(BaseSetting): - def _parse(self, value): - return int(value) - - def __int__(self): - return int(self.value) - - def __add__(self, other): - return int(self) + int(other) - - def __sub__(self, other): - return int(self) - int(other) - - def __cmp__(self, other): - return int(self) - int(other) - -class FloatSetting(BaseSetting): - def _parse(self, value): - return float(value) - - def __int__(self): - return int(self.value) - - def __float__(self): - return float(self.value) - - def __add__(self, other): - return float(self) + float(other) - - def __sub__(self, other): - return float(self) - float(other) - - def __cmp__(self, other): - return float(self) - float(other) - -class BoolSetting(BaseSetting): - def _parse(self, value): - return bool(value) class Setting(object): + emulators = {} sets = {} - def __new__(cls, name, default, set=None, field_context={}): - if isinstance(default, bool): - instance = BoolSetting(name, default, field_context) - elif isinstance(default, str): - instance = StringSetting(name, default, field_context) - elif isinstance(default, float): - instance = FloatSetting(name, default, field_context) - elif isinstance(default, int): - instance = IntegerSetting(name, default, field_context) + def __new__(cls, name, default, set=None, field_context=None): + deftype = type(default) + + if deftype in Setting.emulators: + emul = Setting.emulators[deftype] else: - instance = BaseSetting(name, default, field_context) + emul = type(deftype.__name__ + cls.__name__, (BaseSetting,), {}) + fns = [n for n, f in [(p, getattr(deftype, p)) for p in dir(deftype) if not p in dir(cls)] if callable(f)] - if set is not None: - if not set.name in cls.sets: - cls.sets[set.name] = set + for n in fns: + emul.add_to_class(n) + + Setting.emulators[deftype] = emul - cls.sets[set.name].append(instance) + return emul(name, default, set, field_context) - return instance -setting_update = django.dispatch.Signal(providing_args=['old_value', 'new_value']) diff --git a/forum/settings/basic.py b/forum/settings/basic.py index 80c8a62..23427ea 100644 --- a/forum/settings/basic.py +++ b/forum/settings/basic.py @@ -6,7 +6,7 @@ from forms import ImageFormWidget from django.utils.translation import ugettext_lazy as _ from django.forms.widgets import Textarea -BASIC_SET = SettingSet('basic', _('Basic Settings'), _("The basic settings for your application"), 1) +BASIC_SET = SettingSet('basic', _('Basic settings'), _("The basic settings for your application"), 1) APP_LOGO = Setting('APP_LOGO', '/m/default/media/images/logo.png', BASIC_SET, dict( label = _("Application logo"), diff --git a/forum/settings/email.py b/forum/settings/email.py index d95a164..fb49f99 100644 --- a/forum/settings/email.py +++ b/forum/settings/email.py @@ -2,7 +2,7 @@ from base import Setting, SettingSet from django.utils.translation import ugettext_lazy as _ from django.forms.widgets import PasswordInput -EMAIL_SET = SettingSet('email', _('Email Settings'), _("Email server and other email related settings."), 50) +EMAIL_SET = SettingSet('email', _('Email settings'), _("Email server and other email related settings."), 50) EMAIL_HOST = Setting('EMAIL_HOST', '', EMAIL_SET, dict( label = _("Email Server"), diff --git a/forum/settings/extkeys.py b/forum/settings/extkeys.py index a2e8222..abe860b 100644 --- a/forum/settings/extkeys.py +++ b/forum/settings/extkeys.py @@ -8,11 +8,10 @@ label = _("Google sitemap code"), help_text = _("This is the code you get when you register your site at Google webmaster central."), required=False)) -GOOGLE_ANALYTICS_KEY = Setting('GOOGLE_ANALYTICS_KEY', '', EXT_KEYS_SET, dict( +GOOGLE_ANALYTICS_KEY = Setting('GOOGLE_ANALYTICS_KEY', '', EXT_KEYS_SET, dict( label = _("Google analytics key"), help_text = _("Your Google analytics key. You can get one at the Google analytics official website"), required=False)) - WORDPRESS_API_KEY = Setting('WORDPRESS_API_KEY', '', EXT_KEYS_SET, dict( label = _("Wordpress API key"), help_text = _("Your Wordpress API key. You can get one at http://wordpress.com/"), diff --git a/forum/settings/form.py b/forum/settings/form.py index 5cb1aee..7329d5f 100644 --- a/forum/settings/form.py +++ b/forum/settings/form.py @@ -37,4 +37,14 @@ help_text = _("The minimum number of characters a user must enter into the body FORM_MAX_COMMENT_BODY = Setting('FORM_MAX_COMMENT_BODY', 600, FORUM_SET, dict( label = _("Maximum length of comment"), -help_text = _("The maximum number of characters a user can enter into the body of a comment."))) \ No newline at end of file +help_text = _("The maximum number of characters a user can enter into the body of a comment."))) + +FORM_ALLOW_MARKDOWN_IN_COMMENTS = Setting('FORM_ALLOW_MARKDOWN_IN_COMMENTS', True, FORUM_SET, dict( +label = _("Allow markdown in comments"), +help_text = _("Allow users to use markdown in comments."), +required=False)) + +FORM_GRAVATAR_IN_COMMENTS = Setting('FORM_GRAVATAR_IN_COMMENTS', False, FORUM_SET, dict( +label = _("Show author gravatar in comments"), +help_text = _("Show the gravatar image of a comment author."), +required=False)) \ No newline at end of file diff --git a/forum/settings/forms.py b/forum/settings/forms.py index 337dea1..aa5a352 100644 --- a/forum/settings/forms.py +++ b/forum/settings/forms.py @@ -1,9 +1,17 @@ import os from django import forms -from base import Setting, StringSetting, IntegerSetting, BoolSetting, FloatSetting +from base import Setting from django.utils.translation import ugettext as _ from django.core.files.storage import FileSystemStorage +class DummySetting: + pass + +class UnfilteredField(forms.CharField): + def clean(self, value): + return value + + class SettingsSetForm(forms.Form): def __init__(self, set, data=None, *args, **kwargs): if data is None: @@ -12,16 +20,16 @@ class SettingsSetForm(forms.Form): super(SettingsSetForm, self).__init__(data, *args, **kwargs) for setting in set: - if isinstance(setting, StringSetting): + if isinstance(setting, Setting.emulators.get(str, DummySetting)): field = forms.CharField(**setting.field_context) - elif isinstance(setting, FloatSetting): + elif isinstance(setting, Setting.emulators.get(float, DummySetting)): field = forms.FloatField(**setting.field_context) - elif isinstance(setting, IntegerSetting): + elif isinstance(setting, Setting.emulators.get(int, DummySetting)): field = forms.IntegerField(**setting.field_context) - elif isinstance(setting, BoolSetting): + elif isinstance(setting, Setting.emulators.get(bool, DummySetting)): field = forms.BooleanField(**setting.field_context) else: - field = forms.CharField(**setting.field_context) + field = UnfilteredField(**setting.field_context) self.fields[setting.name] = field @@ -60,4 +68,28 @@ class ImageFormWidget(forms.Widget): elif name in data: return data[name] +class StringListWidget(forms.Widget): + def render(self, name, value, attrs=None): + ret = "" + for s in value: + ret += """ +
+ + +
+ """ % {'name': name, 'value': s} + + return """ +
+ %(ret)s +
+
+ """ % dict(name=name, ret=ret) + + def value_from_datadict(self, data, files, name): + if 'submit' in data: + return data.getlist(name) + else: + return data[name] + diff --git a/forum/settings/moderation.py b/forum/settings/moderation.py new file mode 100644 index 0000000..6af5401 --- /dev/null +++ b/forum/settings/moderation.py @@ -0,0 +1,24 @@ +from base import Setting, SettingSet +from forms import StringListWidget + +from django.utils.translation import ugettext_lazy as _ +from django.forms.widgets import Textarea + +MODERATION_SET = SettingSet('moderation', _('Moderation settings'), _("Define the moderation workflow of your site"), 100) + +FLAG_TYPES = Setting('FLAG_TYPES', +["Spam", "Advertising", "Offensive, Abusive, or Inappropriate", "Content violates terms of use", "Copyright Violation", + "Misleading", "Someone is not being nice", "Not relevant/off-topic", "Other"], +MODERATION_SET, dict( +label = _("Flag Reasons"), +help_text = _("Create some flag reasons to use in the flag post popup."), +widget=StringListWidget)) + + +CLOSE_TYPES = Setting('CLOSE_TYPES', +["Duplicate Question", "Question is off-topic or not relevant", "Too subjective and argumentative", + "The question is answered, right answer was accepted", "Problem is not reproducible or outdated", "Other"], +MODERATION_SET, dict( +label = _("Close Reasons"), +help_text = _("Create some close reasons to use in the close question popup."), +widget=StringListWidget)) diff --git a/forum/skins/default/media/images/openid/aol.gif b/forum/skins/default/media/images/openid/aol.gif index decc4f12362124c74e1e0b78ad4e5d132621ab23..dd629cb68a515201dc6a1930f156fb8a644a0fb2 100644 GIT binary patch literal 2116 zcmdti`(M(B8VB$%h=5$|D`Ekef>*qp!j7F$mI2`$!T=o*5sbs1UhYR_T16z zwWE3AP9;81N!-y)+|}Z}yTxbUMaq9w{spSQ{p#Rv+NcF>p$FQ-4z-6L>R86RMEmwq z1n<)F!%?8`qHAnPS8Vb1_0r#x zez>ur^v0(DX_8N9H_LSCW!>qgy1)FfJEOcO^R#~J89n!GZ}v~U+s^goo-^cC8uwf< z?UUassJXqr_VGbgIwZ}hSe|-M8C%!x-)j!Jl5Gic6H!I*WK~! zgX1^uO=#|qYwk~IAH3{-=rq~;=%xPAr2g?_@8c=MlPTlSlxgVI@6TS}vAi)`UYnhB z`0c>R+riPd_pNX5joR*6Z4bw6k6yfcGCuueV*2UC^s|?LSf-rp!>{bt*YC&Pz8`z@ zVZ!#|rS0S7^v9`b$E#__>py1R*k^3-XWo67nf@?q|MymLGL?-Txa!@q9hi~0hlkKKJ8s`6$R+V=~%F`lqCU^WFFFha_@n- z;EQyf-nZTsAVqc)`3#g4W8Vsab7e7mU`D@$g;)Y8z^QqJEU83OfLano@QC$2l{aua z@W@1&DL5-qiwF*(*o~eicWIaqd{TtW{~s_pfVn}$C;HJ0>$`(D3Kg7n^_eUfCh2Iz zOho?V zxM(8aC?! zO&y9l#arATcI*HM#YW@RYr38*uYX*v-``m*1W@>%j7%V*(ThGi?;QG*;3_p}Podn$y* z=rv|Y4f7`$g#pE~$#XUo0T6h!B2kyJt4W;Whnt{d@2=yB;-re zJ4Ym~T(M6i0H8AkM-Sx*fO+p4K0I8`gK{*OX-@qndiKr|3Ljr=;8WyHUf&v+UM8+| zvkHpOSfv-{GGoOG9hTIo!B3`xo9Gm1AQ^*3Yhmsfvh$;8nxa8*jH+PazAA=`kA=@# zC5K=gd;&F6RY{2dl}tSluacev!=v;k@hZ9~~t$t4@M=y2!hdIyYk zJ54om?m@?k$TEK@aR@-rWLi(82dYPHtss9XUL380dMPYOUOdb@LDE-PX!R1(mB3`fO{2+z8Z|AZ@3=aAvX literal 2205 zcmV;O2x9j~Nk%w1VNn1b0QUd@0a~U0{{97Cr-7}~`~3at@%IK^rUhN5{r>*;`TQ|@ zxAXV<(c$m-`uz9${QUj?tjXaAU#9_Cqyt>0`uzP6Wve@Yx&8hA@b&ra^Y|liumW18 z16!pEV5rI5>-6~hXr0Mflf!YN%x>+Qkd8p6#`TXYZ_T}#Ooxt1~X|1!)YIhOpD5#NM*a<0W#j0a&CWZm>^{ zzyn;Rz}D!)*y=NTw$$VApTXTqiN5vt`#gcU#o6jVg1UmP(x}JZzt!kmmBs1t_qNaF zEOxXxf4M<~yV&LMwa?_c(&n4K+mpH2M25T;XRHups^01ID|NGbsn3_Y+VS@KWSYp* z;_ryE)EsK9fUMC>i@&bQ;tOD?Mu)vGc(qZE!4hSw$lL2DbFy2M#IVcb=I-{7w%6?P z_}%F8#@g%I=JCVW>A~0O1zM$Emc;;Aq|e~)=kN9^bhB5I!`tZcY@o~4Dwe zHhs4lW~<`t^hk)l_4)e+UZ(8x_}b_3h_Ter;O(~1GFN6(4WBES(L=`_W7sA;8&5u>hkx|;qKw=^yu*R z4rHnaUZ-7_#k9}l^!NJ*UZyBu2wvd-h_@b-M1#8yVW@Sa&Gh*C zz}DxCwAK`8te?W&k+|6D@b~`z|2%=Z24AP1z}wd3@G*F`*5vQL)#tg==7q1*{{H`_ z#^61Iy8r+GA^8LV00000EC2ui08s!P000R80RIUbNU)&6g9sBUT*$DY!-o+6Jy`H% zQ^boHGYUk|VhNoVyflg&nXrK{5cMRHfHRGxOOgqmlo8oERHm?$HPJbz#x1Z zpeoL(7W@hZxJ5+J9eE5BP*LH>O&${ zj5jX`S&`8IL!Cx*Fz7_k)nS8ApeE%1x(B4-jBvRPK#9X9Oc*K+HhF5Leg{%TGv{D8K;`L_xwpaHxaF z0b^+Mfl?X7amfU05OYC(Eiz<;2RbaUiytGQ0x5J^bziwedW^uQh}2wH>_T<*uvM9pjgf(kHL(TyFV3}K8NEHsip6(+=y z&mT8*M^FIdM4?9wB6I;j1ZWKZ^G^UCv;lw?B2a+P3?fKiKopBK>LNoIcyNRiBZxr* z3skJY1QoA5aVRDuT=9ep_zX!<2JM_N0V+YPFa`jiSn&@6Y-j*L7D^;UixL1NBE%5F zIz+$0D>L=cJ;YWUy}E(fIl&_o8nn81SpJFsxj2TRoOLkeItpmjn7WRMLXFdR_93ql-= z1sX|!ae@q8)L_8?$rNzmK!~tV!vO{eV8H9GyWWQlALOvc;zfv35CtA!F~I>nUE0WC6>_LJDwEf?Ad!6)ETg5hf{+1h8Trx)^{dte^urh{6TSDkj1^wSz7qhJ~@?gP6d01s`$vw|#jWKz5GW2-x0FoEBpTS&Fpok-w*Zs0Yeh8?4|N;Pa^L_{*;0ANBj?|Nr>K_Bzw=hOXfr;Pk5a{Oh&kHLBng*6@%0 z{`;=sA*7K8;wSd}=B?o#z2{2X@}2Mb)b9Dv%j|F9^Qpk} zEWGm~x$+v|`i;8s9l!J}H83o60001-NklYd_hr8+bqKKmwEf|DI$sP0z$SzOSgIKW(q;=3VZOk4xmq(A zPY|Z+EQCmZfG`}1A?QLI!Q?HL)EfxRmcqcOAXIC>K+G3>C~|rTN+PMIeaK`rgfhqX vc0q9!Wc>2fPp;8fYoKkH=WYk=8i_T{= zxi;G@ciLRWhGSA$QjH@~OKXWG9bbL_jPrUtAMf8^@5lSS$8)#KFQJEk&A=k?J=VKv zHl!?!XO^w^O*K{<{w>tD5m^y?7hIailF$4z_`kw_{yUftBxjA9ZRS_o>(1{SQB<>Rc(0E z{fH~-90$>toaT@i!jCXFRw_Yxpy( z>C(-n%eR^@)0>m%%~!HpuI03nb6d%IZOQrVDFyARg%8t;1?k0tj1pmHX=m2mu3KOi zozb1m=*ebw=a%*4uzK=XqJoOPqDpabwfIi8q@+etQY$H~?f>ik0LUHy>joK|!7}dO z<@G}qd}-x_;i`t=s>Vk(O^@z3kFZ-t>e`-g+n?|{p4NAa@&%&}LRn+yv*zxxR?+iT z(RiEad3*2pL-7lt7;o0AMvBf{*izBA5RU8ObtGs`uoZ2q0wpS z==88`dgR%gM`LeB#%3Om%{+NN`*eJE^u^oJmv3b+-adQvc1-^6xm@vL^1b}^+{E;U zsTt+#*^kq2SE**+sb&?QW)<`A=H?Y2<`r|F-zz`QDHlGd7L=+*)hD%TUj1p|%lv}+ z^TL;f#id2{l3M-s%hIalugl+-m%n}c_V2fUSF`f}tND)%%P%kp0IUFZ-}`+32>`JI ztRT%u98j2yFu(=(aTr}`s7-rINL*$Q&BXCot$#5i0j|9y-x6qz|-CyB?ow07_cWUYygAx{+Kn*A| zu=Xy*if+wY+IiZTDI(8W@SWEwVDJa6dwwM@w>>dP0&XPRP*#}2DZjK?BdFHNd zbTGwmJtSgE$0l>bVSbIFC3XY4HATtDW*>C*X!MYVkK(ZfK1<~wh^#zHK7Y1Q4mLs& z8!glcN8#0<=7U7=*_U2mRbYhrf@0-uz_?8i5FP>8q|8C7D}?4|)j zWsiq>;TlR3x{cWmkWQN>64CvlbLz>bI%iocJTJ?AB~qfEE6TDD-y$bpk2)z6bfI>n zFuZII)W*Q^r6_{6U1%RCEh_RLw;va+h|${0&UCnL+n^M7nbj(Cv`pDMA!&G4@JB%! z`lcbokg14;BXvW_MBN_;CIZ)u%!MUucwJG7@RiFGBI^MxrEhD6+Lvs^=4OlVcTKVd zdX-YEUNb&lrK#bW#Pc#Y{-954PE1q^NLT%pKzpm~qY!b%B}csZ@?31U1zYIGftm}r zI(S}_-y56{CYFZXVTekNI#+mHs}=QGdy$kPqBUD)M!ilJeA$p-*Av?B_F z=6*FrQz~2Us7)4V{pt(R+Q6Y~G$CkhoFV8D?IEyuG-5BBsGG0}VgL}3BqaoiS3*o- zmPk(-0ge)^gQtV=J!%ND+Oc%)xE!%lIRqtflFd^9ho5DTHBMxRF6X|6Lk$_WUTIcE zLPHRT0hm6WJVOE?g~34BJ*X$l8r+WZ1-7gM$&Nmec}cS*jDxt^H$OIA+ZR~u(EqVa zrouE1gnQ{%^&d+}Ddbqwetb-Qw+Rg0ZKx z&@oVwYk{zTlDlPmt@`@>gqOWSUYhOj^{u(ngOha>|?_PGQs<+b3)a1g<-&k^{S8%1h$J}*`wztCBw7}OfP?3F+ zx}UAgE>4j0^Z00huWN#^P-~*o+2@|D%bKdmk)p&^Z>3FWpl*hPmxpaxO{r>*^{r&3g^y}{QvcA^&`utmTsG+aT<>~RIv(NVT z`dxOZnyJau+UV8W=vs5AIaif$g|m8&xL|mzI9Ha?*XCe(t6FoXft9>`kh$96>;C@# z=j!sh#oF=n_nfQBkD$XkS(mT8)w90W`T6@dRg{;f$iKG0|8^T^QRhnm2|&EM7A>5!qsI$4%mbf-I5mPlls z$SB4TVtT7ycB#|a=rU51xWw9-sL89i z(n)2W=j-y--09cc>DS%rdyu)Cs><{B`Ju4Qjh@49g|hDO^}EK~dXKnYc&bxvqfcs~ zPimqoOpejm=F-~dch+4I98VJ@Ad5N^-O1=?C$GIVlOYcaH&31>#Gvky1T6k8 ziI9>}$}k#&1aaV#k&~HkVjA>Ni}1-ALH^X7g3wDVMhx42p^-3zFGdr>{P}}0nBh() zQ+&Yj_sGEtC{9v4!QeCs7}ZYbLV2PZ!;@{5(yUo^Ep-=xT4<+!GBhd%)x@V1(7dY$ zli|e;x|GU}&KlATNQC)RW{;a~Qea~L*oW%bl|LfzM8I*SAGLi>0OXdiYZ)0oD=NA0;?e(m_y^K!7e(_>)B@sen*VB(K|;J z;Q||($kIn4;0ypl4t>-S3JpnY<3$=NxKl(oq>Qq{7C!8dNgbK+K*j(AkU>Kvb=0s! zARZvXTnJ~JVgmss;IYv`U6h~*6>vQuM?3-$A`TU3R6z&;&1B&ZBQIn@$Qv#gGKMd9 zNHPWw)!<@i^F zkW_BOA%Q=nA4{0yNA2%5#gj6X79!bLy&P%(i8R0KhQ33|Ze zj|Ok}Gsr!L_`}03U?j9t6w0L1E*sk@0ZAVnNaLnII^>|)C;T=d!X^G#Ax8_0R3V8E zl#H>)GOFyNnF1*IGr|%kTu}@aihL;wA`4(5gFkFQBJu~CRj>duDkBsH2JHIN$u@|5 zfkhT}5Tcn5^JsPl5SsM=&8eIT7^H}xB?~M@+bUFBMfc8pJwdwjAuwgf&OR%CSahM z26#XX{?G+}HV}YBB!VF4+r9Wh!XGIp1P)5^3=c%-A5dWe2?B5h9F#y4 zD_{U7W^fAzFdzebFo6j2V1^USVFmb*0z1fH251Z+3}{G#8M>GS#jc3BDyw#OR%uR*2@iO~#K2P{Dxa;V!UwdH!PC{x JWt~$(69D+RP9*>U literal 0 HcmV?d00001 diff --git a/forum/skins/default/media/images/openid/google.gif b/forum/skins/default/media/images/openid/google.gif index 1b6cd07bd8b9f27175e0a03be3b5849e0f5479c7..be451e56d4b1236c6e231d994ec64fa6625193e4 100644 GIT binary patch literal 1888 zcmV-m2cP&yNk%w1VNw7c0QUd@4l#clIC>&HbO>Jw4_px)T^j^r1_^!-C}k*hCK`J| zIeKRseQrEKQh5(GhZQ-85j&J0K#3Ybl@>#%7fH1wNtiTFhdWo6B1@zrQ@b-=xjbdP z7)`<)Qpzh^#X4rkH)+&JTZK_(mPux%MrycBaK2S;r&@QnYIc!kd7@!?uyB8)Luz$^*IInXP<+=9jTIA;8ylJ;DV;D*kWWv=R(pb!ZH2gVgQ|6jw_JkIT#M&rgv@4% z)@+d8aF5Y_mC1RT++>a5ZJ6wHn&^F|@O;&RmK6k=A`*%=DySC&wip7fB@@3mB$Gxv zn0pJ5d@-v-G`dGNjZ;dDZB&zSXuDiYrDIvTb7-@Db;B3|%_IxODip^t9MUBW(<%|o zIV0phC&)%L*i1OsQ$OlXHo#a(!d_6=T}#hoRK#;==UGJXTSMz*QS@R>^=nn;d1sw~ z5s`p7vyKF|oI0wQRiT)Cxu;^pn*hn80@SPm*RK=Mt|jKS0^q(d)P{V}x@qo-dH9NX z@W~VJ$|v*7Ed1Xk_|!Jvz*XAAYU0IAn%FEHu(bUk+*w)y>&g#U{@5a~m+uY;M?$6KW($(|W z-s0}q;`Zvz$>H4G_{qZX(9QSU+2G&d@9EV4`^x?G(dp>v=j{3A_UG;K`S$AR@b2*Y z`Ro4v{r~^~A^8LW00930EC2ui08#)Q000R80R0IZNU)&6g9sBUJeY4?I&}*p;*;g0 z1q&8FQkjd$v7^CSM{FFx<{-f;iEQ7LGvBOpzW@!hC_mfdr}IK>)FoL=72MBty_WVQi7aPW2EHg-!$| zvIia5^yC5xZ}rfD1cuQv8ej6TK}CfEMYBW#ofPCl2M^%b(I8D&L7hO&fU!i6k?c{$ zT>fn12OW0!Q1R}6$ zkUpFF!pkbV$U`Te1?|%Y5>(v(Lx>)894QbYc`QKA4;(~rqzeIn0x1IG_%lcsPmnWD zeF(MF0R<9#W6~-?m{NobFv?>F6R{ff#-8LHq6ZYV`qKyJp_2gx6az43iBsWnck{h63F~ zHXVU5&MfB`kwB2XtwETQ)pW2_+?Lgb)e0R$6F;6V$(myp5@ zmq6&xJfm1~MHaz$liuN($8v`qcoc&THoM&Eix4l6AUFjZ91hMUm4HGHoA;Dr1r=0S z(aBH>tyhXBm}s(+fBqfpwm08^hdch{KnFp7;er#q!at!f!Qf7iIimP)AmWHcDTH#T zh}>fz(Cf=VegOj!2n9Ur(E$Y>P%aIcFd|04041Pwj}|zfJSU9dLe3%qO;peyLU;fS zWyr&WfPh~IVF3y~s?ozD8Uz)+d%-yd(vBUZU=;H(q7(^B<6OWQKZt-cb3xBiE^HYQf=ZKSu)*APi@FdDnu7l zbE$~JN>R#|+lE{hiz(BRl~d+uOl9Xe=bt$55AUzjP!`6aB*XlLd;L`$MA0-=oh;_@%=DSKzTW5!kkg02oxN<-;QgzZXZ6@(M)5 zb-u1@<^vWqN`U%R3CtOJuojJt|Ec%yo>_yZ%MV%ajx`i>ysy4d0^mKeNIQ#t@2y*KMj}jE8i*m zl#eQZ7!1Pfn72wr)$QwY6gabm(O6$Lylw3Pd(s%KbU5crNh3{eeZxI4(dJKB1MUAN>n;QZCCkQs_tGtJY-^iC3ja3x9v_uTP?vFVW;}s># zx;OS=;!{FWer7!3y~sj6t|9~xWcTkC-QJ+z*` z{$kU`Oy-`7hp(oZDFCEubef~t-M)K#*_S^K7T>g7T^A0(oK7oIN_s0RehD8}s1*i- z0sK!m=+Xa`J^uB-PXLSoRAEU$)j~-M#?mcPT3yt{hn6u4gKCOf&J$L|DbuOJmz+s9 z2lJ2J?sjAok}A_-F35;rl&1P$i@IJDD5Bmxks6yO#s#L4K20(2&;kgjBXbWq$Y}&> zCc!g3Y<~;U$CQlDX2nU|3Dx218gg&b%2GehXilrp4#P1y;weqVUjQptQlB1c;VO7e z@*D^wqyiQ&MP@@Eiijj!C6&AUPa6q7&lm^MToqj2ny^P9>WX^Ttd-e`;&k}6HltTz=X|A>@zzv6@ z)2r$ZZK19Hq1s>pg(Wg*yF5bqcpgSu2*Z=3Y48rhsMseSGm6D#RWnKSz*rI2&e%)U zhJ)uMj-*j*Cq6a|SR^kuis=outMPl7vPf8@-OF`fK(;t_tEV6%zOcxe#-=20Mq7|L zi=0@wEQshFjSRBhL&)v46Z=EWNGr^Y7(cjs9|q}uirjP>UklMq_Cn}BpIZ=OxlIZN zrqgmZ9Daq-5mP(tkggoG%l1Mp5`!YX9P+{9a@vc{UA@ux9w!5<$J7Qsxz)kjp9U>O zdMGwqZ?ce!P4?kRO}wMe2<(jAw{y`J9zkIp$bd;9jpjy5e7h8h$r8hU6K*}g{&bHR zc4&^sGRyCwh1g@leXv&EK^8KfI43fLjtGZ4%_yBJq-Q6Ii74-1r$U?ItVA?Z^o7I@ z6Gx_VGVMkkya7VbaJCw^O!RGWg$&GOOC(&Bu>?}_7>IP{%ivHGBI}%WA6YI2+3M9H z%iJLj*4d~I--NgTJ(9t@qodNU?AaV`YHlKR%UVir5C+OujBse~Mp}pFlaUEl5;MVK zxnv+FK^_1to@fbJn^<{1g0s+?k E17=sO82|tP diff --git a/forum/skins/default/media/images/openid/livejournal.png b/forum/skins/default/media/images/openid/livejournal.png new file mode 100644 index 0000000000000000000000000000000000000000..9f2f6dd8d5de00d094b0a153a53f0b5c6dd0dfce GIT binary patch literal 471 zcmV;|0Vw{7P)N|JI!F%9$5NeNKtPh>ec_*P8#%n*Y|ozP5wvQL7un$7#B+5ex^|1VpTYoF3eeXvo3w3^NOqSgPI&H5)(h$BI4Enbjt zq}WS-u}^`ro6Y*6)c=~!`Y2S2X|CRRAN$TU1EXx3Z-+&Z+zTUFn^>zpT z5Q2vz!0CJ;!4>E0&33o{2_hd)rnC8CNz!b!JKbJ?FeGu7Sh-TI)f*(KG|Oaj`9hH- z7-ErVES^Y;mdow&`uqVgZZcb}HoJoWP@~o94MzC_hDVBue}eQ|#t(q`4wD!YJd6MU N002ovPDHLkV1k6v=X?MF literal 0 HcmV?d00001 diff --git a/forum/skins/default/media/images/openid/myopenid.png b/forum/skins/default/media/images/openid/myopenid.png new file mode 100644 index 0000000000000000000000000000000000000000..e5df78d03b39a7461a3a79a91901c25f154b9eba GIT binary patch literal 686 zcmV;f0#W^mP)Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;UJt{NMlp0tZP% zK~y+Tb(2ddgi#d7uZ7(#l$9x@upy<$BjwdJ~8N|A+_4N7?|l*L#uYLaN0 zqS*-9coUP&%tXk{tSDnJH8}i_?(}^gr*3`se)s&&|2%F4eVI_0%j*}my&GZMzY(t6 z0RM@OiWK?`tuSQ8i=s5G^12WJz(Q~DG;BE`O|_YXet{zEXGzmB$qU~&!!nF+koVzP zx)B53-aq(9MA!{9G$Z!b>yoN}C1lfoT=Tv(Gbsde-jBAz2aup`dnWh!7d(dG-CN000DpuL_W2YvnXbwt6`gRR3oUkOBbc3IiZR z77)(a_D)X2>fIp7{||s}FhKSn2T-u-J5|-dbZ7pv2533zC(sHx1CajH4*ka}fRvBU zaR)JlO!_)VpPHiJ$BT*~0TpgN4-)Evlo9mo>i&BY@quANPWMm2Uw~p~0Re#VmhS@7 z8y;nRY@T~4u4O4agv%S$L)}OkmW0 z7xLfVQstk0g*o#KU#mfNKdE^5na#7dVH$^Z^iG&oOc*frY9CNdhi@(JyAEi zQkQi-cS2+DqgKuEqyQ#C%Z7=uj7BnY9WTx`v-0^co`@E|_vR13kUL+>9JU4?mv3DA zOg8?%S#u?#VJF*7h9@%^CBHrR%(@{v>)efo!yKwS32Y4}jdSkR9+Y3hA+pq<=Uplf zvjbNltK7C1E}oq`9!XEP&{(_R32P89!%mSob0!ok8@4s%v@K<2n6^?c(7V zZ4oVPm9`c5MXgm(Hoe9}xV4T%1orlCy(_T zwHoF;JT3d}*8aOL=`HFCjPHD*Z6r+v0{IO&q1)GT*|3!B^7xGZ_s}lsT=A7650r|g z$o-RnRfDCU)2g;x>%ao+u5QZtgT4`<^Pfz-5kJk#bp2|T6-Uc~ienOzsmdio!N@=6 zROyD-m{Q)TSL$+c$f|c1e^tPi+6S!zBWM_LR)1h6p0!moM~h(O2Awa_TMenjwx%-9SD^E&4Z@mHP9(;)R8~klehkMRt47A*CV!Vb6~M{#%@jR+}xbY zSLX}WOre@yt|&Cv5rO845qUD*)SphvH+fNthnm}YtF2pws_`{>e_}>JAzPJ=%@}CR zm1x!a=1K#~Ff=;}=Mf{_Ic?81Y+fvGktPiB1UnWJ#wx=K6FNnXkyp8F_HZmqhsaSt)pu%I>;yr&`m# z7tjFHUE}iHtiPEAn_70lY7UcHvVCj#R{Ii1*Y}gDsIw83YfBcK(Kp3K>*9GdC*vY*r7LGL$ zsvgv-89fY5M1V}%S5#`86AD-n8PvVhh@0dVr#&J@HmHD|*Bl+=p*g2tZb~-ylolYk zKp2`2a&4gbf4mu*aE1awN*l+uSQn*6Gu(V_0zYR3oWT>AJnuV%dHx@&0+tN`987s@>!&?d*mk@$_xaPpE-pp!$BP*P) zgA!jR*tx$a_NZr(Rjl_E{h(PW6v8n+U`^QV_YE|Qe^o7WoyEkON2mQxjYHi`3!emu z+zI@BPJtuMozSx-c5darq~!W!Aoi|TAGx$eScELOT&`5|R5n@%qZCnus6UMalW$4+ z5w1B8=s$9hQ%R7shHJQR3zKIWl?RoG6TUgwRPsT7pm}v65a4X07da6ia4TFqs^3FkZf%Vj^7 z|8FG_Ac$hse?6-ZAOI8lNQh1~?n!y!IuyHpe@sYZ9DFM@Gcz+FlF#SaemKMgtiikB z691_Sj@Tklncd`|#Hw(SIlK|!7{^@4moPeqi(wMVAgh_%jrU`i*pLW6dk?!cCk#8!C2b66_0SYt31Mo>3wpT$n?)kQ99WIE?i zJA#JrlTN3ZPYp&Lxs{>Vhf~m~W$&JdL5HMpX2^q_n-EIZyz-Yy)KQF>YV|QnxjMuu z{VJ~lR2=wGs{kXo8*R2Ce;A5CEtvj&e?et#Yq&g>VcPp1vLk5lsfC3F9M8T3bQiTc zr1T_|xDKmtPWc73UJgXQE6AV6^!mpuEF&FK1k-u=VyCCN*KIkZEY>$K`{=+5Oz;aPEJfrxGO)0$*H=I&)fH{#l^+h z*;!XL!XUmjSqon&2_oDctP$i`*`QZA>dF*5w`4NOskkWUlmf9?H9tQeD_}H4Ae+rD zEiJivgmG-PJDQnRkLe_CE%Zu@aVV~sU-OVAk@7$BKZ{CMXXG^{6*^D31} zk@JRzhA^6hhXP)qoxjW_7ZTJh=JRT0Wo2q=YGh;thXs13udc2#4k+m2FSA%}Y;24N zUOA=6kULBrup$FMs51fmR8XlPz&MMe0!b@O%pTTf3moC_@Gx Qd$`e_3d;rMCH$ zVSohC*nj4;;^5$5B9Z6}wXUu%cFukbEfiyMUtixIuNk1&iU78DcXuP-)6>Hv z3W3R&y}iA>1z98+$3A{Jc_82wv(U+c{r&xz!3OlhO|b~$0Nn&=tiZ~w02V9@1=v4p znqFI9vM9hwdP4NKhYMIg&ml$XqgMi<@D!GQ0;T5fjw71@uD*v0VebZx5E6K*V zqqqBr7g-AIjA-stR=8%jP~`+7E62Wd*J$24@<;C0&SPR9qO93o`d9V#H)ysz?*9WS W&RsEk?V4Z!0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf3(rYJK~!i%?V5XR z*3}uupOzLVw_czvr6Mg(q+BfJBAtnwnhhP85N5_MMqy6CWenXVS+k6ocCk@F`KpDYy&d* zrm^YBv+>x~<$2W6{i0#u<@P%U?%dX8R`^eZPDlH^|N1tw`C|q~s7fsGo@%qZGR)p# z@6K|w%`IjReqdHM*la)X?#L{&k!gwYR5Y8-1N=Ed%no#$)yy=@X3apiz!Fd45|{Nx zA2A4F&JUazd(M~@p}h0#ExjFP?>(Jn6BxUgVm6GiV{!8MRJiwCbnQiGMnQWFCESP> zfS5wuK?YO!QAYG_Akua&mJ?yq;cVK;hPi-p8mr>Jpz*VpOkt4D3q8q=@QSB5s?JC# z3;(`GDwa9wRQ(i08kP+Qk-CCppyYyudI^uT4cS_ zS$csec`_L}fQwZUmo}g3>2EffRWR0{b}c1aHlY1us@abV&0e1p<*tkzR-iv<{DC{- z=)$t~2OJl{`g5eK(j=TW3oHC1!Txgx%pM_A`@W>sY%xLn_kgJ;SE@d3b~a=+ey46u z@Ndm|pA3o6#hl_`yPK2ZW@egwdm=H>C54MZz?q;PX%5kNVY`|1V_`aNcQr+}5<8C* zXVvxISBsD{8gUo(2G_%D684h(qZpk)HY^JDV&2EnpFSF0{-Yyga7vKXhZ0424w?mf z0j4f-?qy05&3(ts2_MD4cOxO%kQol*{epAZ3kcU4GO=(B)Do{x(Meg!+HV@omSG$V z$o})`dVh^EDLz@-iubLcj1sNZJV3u6W7>q5`WBQpwOGdOlo?ZTEg6VUGQF5`kTk$; zgJ5TwS@BM@9IUnl$+wpRI3SmTM?aZXOz)lVwiXWky9}CwYHwy4j== zW{;wbP3>mS;?iHa$?R(gy#?X3P?`Xl4NWHRa&z@b?6vA(B{o#nH7W?q??`mv~n)3J%ITg@uPkn>}}HZx{vOLx4O)N_6I?B~p+KYCC6BOKSBB z);?aup2~nMP6eDUfa>ITwn`$H)G9N$)&y|ppvz(|oi6kDt%1@d4c=L-%~VN(o5<~7 zMXN>dU4?b4r75vFYZQ;I(EOGI{edv^M#St1==L&}h0?tN4RLg@JGwt{+gU~N{#Skr zK*jT?@B(?)82HNoh$1$I-{a{UPMWWG-B+lE`~pW70wbpd$uB20}Gm< zY2sW81GhoDmfKOMiwVyHsCZ@|OAw;@p&PN{u#98~e1^KiC4hJaqK|W$r^e$h<9hlx zd4F+&*>axez53$-?6J9?x4><}&7tGH$xjDh$l7_xbz>;vUGD;1R*(q@mqPsG z=`^K7YhfKpMDg_u?`6181-G)DCAd3AAy8zk0_x8wN9{G9`S90rg{WrwIl2;QJM@p` zMdelN1)|2<6oUZq%Ye0<80Gs4-j~wVk|gA0`1Ls!wlpY_Eg-2I`$=p-BK6Fo-|Z=@Oy4ujCn zFr&8+M=0gVYCZ*kpT!b}GT!axL?=>p(dqE_VsQ^+X>XIX6bCh)3-6!cw)3h0c(>US zGOY(89bShx{60d=aOY+1fVmnE{8GNz!d&*HFQWSkNslGQohH>G!8W;i=DWsx+Dv=)3})@bv&G5H&ss(C6hi?RdfCp27L00Uzdu@qQS0@rM>K z-~C#5D-3?havXW^JE+l|Fo7$EkdnLkeKaf=iw9CP6G#C5HX_~vbf~f_<9V)J(p~~v zpaj;(KeDt8&=OrPbpC61IK)aOS*c=w2f}Edqkt(l*N2#L++5b~BZ@xE1Ry1|AO}b= zub7}zE16`3;@paZQGT)vWxHK7ssE@Ee{k4r9Dp3=q!rUC$cbg2 z*cCmDyhI-)R56tEfkM_Zh398DyxQFNqsc^cD#OZIdna8qD;$WHP)5Gp7TNM&L?f z+XjOC8fr2R;#roiw_>4nA3J)>>i6R^lE(|@Q2X8S;)Q=zHro}MXO?7|SF%mO%fzioEX>2NsQrkeR zJB!e@23ZZAo$lf~xR+PIXTx9Z7M{-oWHn>T!jeh6q?`lrJH+~z z*iWql8S3~gg#?~;6)O~d!rz@$Kdr&cf7g!YU?L&aWF*}Hu#M}|?5yaR+Ws@q#IPAZ z(583eLeGpRh(l9H{qX&RDYlt-qq)17TTM(Y&vuKQ<$Ylfe;i}KpELJ3Ys2lm2+Ad9 zR-#yyPU3SbDD9pBRmcQ(IJ$}uT?v}sPTWlo)ntFcjo0z%>s^2YCo6!*J%F~7+gMp< z&(3#O5o?KWTQG)scwNXzdJC8gL{Z3P>R{k6;~qr0<5YrleA7qEqtMIj^8re37;Tp= zav{Zz0kC3Z9>S`&OSZlSWy}P4ue31DDGuBOII20tM|I#$&SJxK&Cs2IPT@r5(Us^L zQR*q^#4AG|ERZ?=uZd02G(uAv0ya_d=*a*74~Q!va}DFYVq*L{1=auM*5C907jof# UY}!*r@&Et;07*qoM6N<$f<-Fwq5uE@ diff --git a/forum/skins/default/media/images/openid/verisign.png b/forum/skins/default/media/images/openid/verisign.png new file mode 100644 index 0000000000000000000000000000000000000000..bc5c5f3e7fba6d7a1d5de5f52c311b5db174eebf GIT binary patch literal 513 zcmV+c0{;DpP)}0AvLQ@%8j|Cno6V=(mN3;^X49e}CBB-C$#4Y7-OMy1bZFSC*BQ-{a$nIzHmh z&khd{?BC#kGBk}tMmRY+rm3iOc6IIb^KcXvT3cHnBO)j$C}e7As#9E)GBm7te6W3h zKR`f71O(>7#)wQ&xwN%qKt*6%TcM(&Zy6lR#KxGJndR2jco!Snzrm$=eXe?ac_k@v z8XL^Nzuqr+h_y?_3ZA?%F4Kp zk=w<`bsZs3P*7_I33(wRehd$vHaM-Hq3PS&S4By@ySto~mA;&v!NS76oTDmiJof+q z0J=#;K~#8NRm?SV1W^#c&{cmJUS>2iGcz;Z|0 zhHt;Uqfl>}CHMIB{L)EKsP<;(yzR#G3$mzSduL^Ua&0g@qpK^&HGKB$CJ!*t;x;#u50MoW^i*d6mOv6X;7Hm~=WlH#ZR8 zhp=h_1%mX@0-U%a3UHfl9~?&&Gr=!V4;pT(SVj8*A0H6LW>&!400000NkvXXu0mjf DlalLa literal 0 HcmV?d00001 diff --git a/forum/skins/default/media/images/openid/wordpress.png b/forum/skins/default/media/images/openid/wordpress.png new file mode 100644 index 0000000000000000000000000000000000000000..f2617056ba02bc3a9623b6f3df2cc1d480df8e01 GIT binary patch literal 268 zcmV+n0rUQeP)#K-#>4krMuGd_4wH0>wBEN%h=_Nsl`lrrI@nIo(*}d0001& zNkl8`>{nSxxEl)jUJk%N>0IK;~Y->IYF<)dhr5F76N z7Sm*B!Cla{j*SIReSS@Qv>}RSd9EW3R_#|4y5*g4Q_QM&)+L@Me3WS)?Kl1q$Rb`r S!aRZi0000{(pi1 literal 0 HcmV?d00001 diff --git a/forum/skins/default/media/images/openid/yahoo.gif b/forum/skins/default/media/images/openid/yahoo.gif index 0f0eb8efe7eb64bfb49b71bcfed741c1193fae55..1ebaa7f46b541d1c57953b8042fa8759a0567d48 100644 GIT binary patch literal 1643 zcmb```#;nB1Hke3X4W{q4w(~CX4FyPINHgazUMs9p*e>W>2#cO&gpuLOt$18m$5aM z8J}eb#bMSAL*~*kMK&gLA4|Of6$Orn2Ar#aDR&NR9co$gF$9AsR_U1#7J3>QYKOKPfXYU<(Cv@g@r z52rJ~VrIByu-sUg?wKqPR;EX0);C#MURjVQ1bIR1qil!(u?g%O-ZyT1dn4N?o8!yL zIhK>-my_$Cn-`FmcOviR$(y%M-pco=+ZS*1gZX?Sza*rjB(y{jDkvqD3c>|tm&?j8mzPJB3&}zuMOYD8Q5jiT6;*X7 z`c8FBb#-iYOqUCezzf|p&_k7oF*1C#f|BW;tX*ktMOjOy?dGW znzEXjvzqU-?>9kB&Ftou?3M?d2Q8cy2}dHykw|l;(mZMFt=6{uwzh&cS-wnGAd?l! z+6&t|xE&q54*6|)C$F=!q_eZMvrEv`UE19v?CGuO?W^qTyVKWS-LI%oC~6f0wF857 zgUWiP@-OA1za9;VhaTU5{G{oLs#&FapcDOHRDzU9Q6qi4Gi+k0ks#P0CJrQ>nV3K#Z)S|Y-jF^}*nS97*>r z#>kN1%P~05gQ=1kWS!lM>xLa_)aru6g!9mjU4Cs6c+_^Z28Izw(2T!7W6U7_!Ldp4 z?ZBXOw~c96fQue^fJZYOEP<>&VVG}RkhHeoXS;m+4Cmi z$nUo4AZ@V_zQd@~?;}srdB`3XNM<_8i9oVfW2e@`o(=_RcD^JwhPH$- z*CaGoD>Et8u~YzrapZ0oVlRg8z3U3+>!YDLrFS-7RW>Ya%!)c3-_6xqp^d=dzX3UA B28#dy literal 1510 zcmW+!eN5I>6h2Lvn6<^!)Xd5hDlfLZ?*XcrP9QW)@-4ktMwaM%)M8CbQ_0*l24$96 zFCxc~J~6$k=GUBkF2{?49T|aFsb(Ko&Z+fB)4rCSet7>lyXT(mKF@icXKLw`+wYh+ z8-~IXfYE>VhrW~BSNh&w=(_*{5C8)RfPw)q2!_Bg7y+YT0W5+gunbnfDmVa#;0PRp z6L1P1z$179&)@~TLI4N|fgmshfuPafpa@E!3@V^e6Ja9)AuxhOGcER$-0QkIY5^5?}#|%n$=&Pz;G-F(O9Af>;zw zVp*(+RdFB=#gRA`C*o8*h)3}xp2drJl>ib@0!d&AB0)t#6h%ptMMYHmf)Ke8NfPmj zhEWs@s6jQPhSi7~RSRlSEvaR-qE^*`I#fsMSe>X-^`IWrlX_M!>Qw`1Knp*qp;Bn<`OCEbSmdY9b-PCpKTi3^Piz?f8 zy?dyt^GwNvp40E_er`*8*@3T<(}!)EUr^Op+Wlj^>bA2Zu5HOanSSmqx^=>0X=Kzc=shR~H^k^D_Zi zm0opINA;Grd+-0WzzmB`9htoIiPY)gdyAhf=<2`k$K9pb0|sBWeR^%q^M`Ymzy0{v zAGS1YzH-Xo)hi!8cI)gxUElrt`{^%}>1j`mD86O+Bh%J=eAmCXEE%zRZiAWIIC00W zAKEkSsqYxqQb6nD8wb6-Eq;Dd#o_vaK4;Y0sB zlrb|aKY!qiCqsAY>3-`Dw8pcK>`aU~%LVGIDmHY4O%6#(r zGy7)b{WdzUcIZF3+Y{BVyg1;&!k_|&&Wb)U7@G{hd*@p61{K8lY zrQfzUZ=akxn9jU!Wo>@vjsXJ~W_|iavS?$;@p-quF|A^H-qD(SYOemd'); for (id in providers_small) { - openid_btns.append(this.getBoxHTML(providers_small[id], 'small', '.ico')); + openid_btns.append(this.getBoxHTML(providers_small[id], 'small', '.png')); } } diff --git a/forum/skins/default/media/js/osqa.admin.js b/forum/skins/default/media/js/osqa.admin.js new file mode 100644 index 0000000..ce0fc79 --- /dev/null +++ b/forum/skins/default/media/js/osqa.admin.js @@ -0,0 +1,21 @@ +$(function() { + $('.string_list_widget_button').live('click', function() { + $but = $(this); + + if ($but.is('.add')) { + $new = $("
" + + "" + + "" + + "
"); + + $but.before($new); + $new.slideDown('fast'); + } else { + $but.parent().slideUp('fast', function() { + $but.parent().remove(); + }); + } + + return false; + }) +}); \ No newline at end of file diff --git a/forum/skins/default/media/js/osqa.main.js b/forum/skins/default/media/js/osqa.main.js index 6264fb4..06b2cc9 100644 --- a/forum/skins/default/media/js/osqa.main.js +++ b/forum/skins/default/media/js/osqa.main.js @@ -1,4 +1,8 @@ var response_commands = { + refresh_page: function() { + window.location.reload(true) + }, + update_post_score: function(id, inc) { var $score_board = $('#post-' + id + '-score'); var current = parseInt($score_board.html()) @@ -68,7 +72,7 @@ var response_commands = { }); }, - insert_comment: function(post_id, comment_id, comment, username, profile_url, delete_url) { + insert_comment: function(post_id, comment_id, comment, username, profile_url, delete_url, edit_url) { var $container = $('#comments-container-' + post_id); var skeleton = $('#new-comment-skeleton-' + post_id).html().toString(); @@ -76,7 +80,8 @@ var response_commands = { .replace(new RegExp('%COMMENT%', 'g'), comment) .replace(new RegExp('%USERNAME%', 'g'), username) .replace(new RegExp('%PROFILE_URL%', 'g'), profile_url) - .replace(new RegExp('%DELETE_URL%', 'g'), delete_url); + .replace(new RegExp('%DELETE_URL%', 'g'), delete_url) + .replace(new RegExp('%EDIT_URL%', 'g'), edit_url); $container.append(skeleton); @@ -100,6 +105,16 @@ var response_commands = { } }, + unmark_deleted: function(post_type, post_id) { + if (post_type == 'answer') { + var $answer = $('#answer-container-' + post_id); + $answer.removeClass('deleted'); + } else { + var $container = $('#question-table'); + $container.removeClass('deleted'); + } + }, + set_subscription_button: function(text) { $('.subscription_switch').html(text); }, @@ -109,38 +124,127 @@ var response_commands = { } } -function show_message(object, msg) { +function show_message(object, msg, callback) { var div = $('

' + msg + '

(' + 'click to close' + ')
'); div.click(function(event) { - $(".vote-notification").fadeOut("fast", function() { $(this).remove(); }); + $(".vote-notification").fadeOut("fast", function() { + $(this).remove(); + if (callback) { + callback(); + } + }); }); object.parent().append(div); div.fadeIn("fast"); } -function process_ajax_response(data, el) { +function load_prompt(object, url) { + var $box = $('
' + + '' + + '
'); + + + object.parent().append($box); + $box.fadeIn("fast"); + + $box.load(url, function() { + $box.find('.prompt-cancel').click(function() { + $box.fadeOut('fast', function() { + $box.remove(); + }); + return false; + }); + + $box.find('.prompt-submit').click(function() { + start_command(); + $.post(url, {prompt: $box.find('textarea').val()}, function(data) { + $box.fadeOut('fast', function() { + $box.remove(); + }); + process_ajax_response(data, object); + }, 'json'); + return false; + }); + }); +} + +function show_prompt(object, msg, callback) { + var div = $('
' + msg + '
' + + '
' + + '' + + '' + + '
'); + + function fade_out() { + div.fadeOut("fast", function() { div.remove(); }); + } + + div.find('.prompt-cancel').click(fade_out); + + div.find('.prompt-ok').click(function(event) { + callback(div.find('.command-prompt').val()); + fade_out(); + }); + + object.parent().append(div); + div.fadeIn("fast"); +} + +function process_ajax_response(data, el, callback) { if (!data.success && data['error_message'] != undefined) { - show_message(el, data.error_message) + show_message(el, data.error_message, function() {if (callback) callback(true);}); + end_command(false); } else if (typeof data['commands'] != undefined){ for (var command in data.commands) { response_commands[command].apply(null, data.commands[command]) } if (data['message'] != undefined) { - show_message(el, data.message) + show_message(el, data.message, function() {if (callback) callback(false);}) + } else { + if (callback) callback(false); } + end_command(true); + } +} + +var running = false; + +function start_command() { + $('body').append($('
')); + running = true; +} + +function end_command(success) { + if (success) { + $('#command-loader').addClass('success'); + $('#command-loader').fadeOut("slow", function() { + $('#command-loader').remove(); + running = false; + }); + } else { + $('#command-loader').remove(); + running = false; } } $(function() { $('a.ajax-command').live('click', function() { + if (running) return false; + var el = $(this); - $.getJSON(el.attr('href'), function(data) { - process_ajax_response(data, el); - }); + + if (el.is('.withprompt')) { + load_prompt(el, el.attr('href')); + } else { + start_command(); + $.getJSON(el.attr('href'), function(data) { + process_ajax_response(data, el); + }); + } return false }); @@ -149,50 +253,105 @@ $(function() { var $container = $(this); var $form = $container.find('form'); var $textarea = $container.find('textarea'); - var $button = $container.find('input[type="submit"]'); - var $chars_left_message = $('.comment-chars-left'); + var textarea = $textarea.get(0); + var $button = $container.find('.comment-submit'); + var $cancel = $container.find('.comment-cancel'); + var $chars_left_message = $container.find('.comments-chars-left-msg'); + var $chars_togo_message = $container.find('.comments-chars-togo-msg'); var $chars_counter = $container.find('.comments-char-left-count'); var $comment_tools = $container.parent().find('.comment-tools'); var $add_comment_link = $comment_tools.find('.add-comment-link'); var $comments_container = $container.parent().find('.comments-container'); - var max_length = parseInt($chars_counter.html()); + var chars_limits = $chars_counter.html().split('|'); + + var min_length = parseInt(chars_limits[0]); + var max_length = parseInt(chars_limits[1]); + + var warn_length = max_length - 30; + var current_length = 0; var comment_in_form = false; + var interval = null; + + var hcheck = !($.browser.msie || $.browser.opera); + + $textarea.css("padding-top", 0).css("padding-bottom", 0).css("resize", "none"); + textarea.style.overflow = 'hidden'; + function cleanup_form() { $textarea.val(''); + $textarea.css('height', 80); $chars_counter.html(max_length); $chars_left_message.removeClass('warn'); comment_in_form = false; + current_length = 0; + + $chars_left_message.hide(); + $chars_togo_message.show(); + + $chars_counter.removeClass('warn'); + $chars_counter.html(min_length); + $button.attr("disabled","disabled"); + + interval = null; } cleanup_form(); - function calculate_chars_left() { + function process_form_changes() { var length = $textarea.val().length; - var allow = true; - if (length < max_length) { - if (length < max_length * 0.75) { - $chars_left_message.removeClass('warn'); - } else { - $chars_left_message.addClass('warn'); - } + if (current_length == length) + return; + + if (length < warn_length && current_length >= warn_length) { + $chars_counter.removeClass('warn'); + } else if (current_length < warn_length && length >= warn_length){ + $chars_counter.addClass('warn'); + } + + if (length < min_length) { + $chars_left_message.hide(); + $chars_togo_message.show(); + $chars_counter.html(min_length - length); + } else { + $chars_togo_message.hide(); + $chars_left_message.show(); + $chars_counter.html(max_length - length); + } + + if (length > max_length || length < min_length) { + $button.attr("disabled","disabled"); } else { - allow = false; + $button.removeAttr("disabled"); } - $chars_counter.html(max_length - length); - return allow; + var current_height = textarea.style.height; + if (hcheck) + textarea.style.height = "0px"; + + var h = Math.max(80, textarea.scrollHeight); + textarea.style.height = current_height; + $textarea.animate({height: h + 'px'}, 50); + + current_length = length; } function show_comment_form() { $container.slideDown('slow'); $add_comment_link.fadeOut('slow'); + window.setInterval(function() { + process_form_changes(); + }, 200); } function hide_comment_form() { + if (interval != null) { + window.clearInterval(interval); + interval = null; + } $container.slideUp('slow'); $add_comment_link.fadeIn('slow'); } @@ -213,37 +372,48 @@ $(function() { $('#' + $comments_container.attr('id') + ' .comment-edit').live('click', function() { var $link = $(this); var comment_id = /comment-(\d+)-edit/.exec($link.attr('id'))[1]; - var $comment = $link.parents('.comment'); - var comment_text = $comment.find('.comment-text').text().trim(); + var $comment = $('#comment-' + comment_id); comment_in_form = comment_id; - $textarea.val(comment_text); - calculate_chars_left(); + + $.get($link.attr('href'), function(data) { + $textarea.val(data); + }); + $comment.slideUp('slow'); show_comment_form(); return false; }); - $textarea.keyup(calculate_chars_left); - $button.click(function() { - if ($textarea.val().length > max_length) { - show_message($button, "Your comment exceeds the max number of characters allowed."); - } else { - var post_data = { - comment: $textarea.val() - } + if (running) return false; - if (comment_in_form) { - post_data['id'] = comment_in_form; - } + var post_data = { + comment: $textarea.val() + } - $.post($form.attr('action'), post_data, function(data) { - process_ajax_response(data, $button); - cleanup_form(); - }, "json") + if (comment_in_form) { + post_data['id'] = comment_in_form; } + start_command(); + $.post($form.attr('action'), post_data, function(data) { + process_ajax_response(data, $button, function(error) { + if (!error) { + cleanup_form(); + hide_comment_form(); + } + }); + + }, "json"); + + return false; + }); + + $cancel.click(function() { + if (comment_in_form) { + $comment = $('#comment-' + comment_in_form).slideDown('slow'); + } hide_comment_form(); return false; }); diff --git a/forum/skins/default/media/style/admin.css b/forum/skins/default/media/style/admin.css index 673e9b4..4fcfad5 100644 --- a/forum/skins/default/media/style/admin.css +++ b/forum/skins/default/media/style/admin.css @@ -2,7 +2,7 @@ border-spacing: 10px; } -#admin_form input[type="text"], #admin_form input[type="submit"], #admin_form textarea { +#admin_form input[type="text"], #admin_form input[type="submit"], #admin_form textarea, .string_list_widget_button { line-height: 22px; font-size: 140%; font-family: sans-serif; @@ -10,7 +10,7 @@ color: black; } -#admin_form input[type="text"], #admin_form input[type="submit"] { +#admin_form input[type="text"], #admin_form input[type="submit"], .string_list_widget_button { height: 28px; } @@ -30,4 +30,19 @@ #admin_page_description { color: gray; padding-bottom: 20px; +} + +.string_list_widget input[type=text] { + width: 520px; +} + +.string_list_widget_button { + width: 28px; + font-size: 20px; + font-weight: bold; +} + +.string_list_widget_button.add { + position: relative; + left: 554px; } \ No newline at end of file diff --git a/forum/skins/default/media/style/auth.css b/forum/skins/default/media/style/auth.css index 3370275..0734ce0 100644 --- a/forum/skins/default/media/style/auth.css +++ b/forum/skins/default/media/style/auth.css @@ -3,24 +3,42 @@ padding: 0px; width:600px; margin:0px 0px 5px 0px; + clear:both; } .provider_logo { - display: inline-block; - padding: 4px; + display: block; border: 1px solid #DDD; text-align: center; - vertical-align: middle; } .provider_logo.big { - height: 40px; - width: 90px; + display: block; + border:1px solid #DDDDDD; + float:left; + height:60px; + margin:3px; + width:110px; +} + +.provider_logo.big .inner { + display:block; + margin: 0px auto; + margin-top: 18px; } .provider_logo.small { - height: 32px; - width: 32px; + border:1px solid #DDDDDD; + float:left; + height:30px; + margin:3px; + width:30px; +} + +.provider_logo.small .inner { + display:block; + margin: 0px auto; + margin-top: 6px; } .provider_logo.selected { @@ -31,18 +49,36 @@ display: none; } +.signin_form { + clear:both; +} + +.signin_form fieldset { + padding: 10px; +} + .signin_form input[type="text"], .signin_form input[type="password"], .signin_form input[type="submit"] { height: 28px; line-height: 22px; font-size: 140%; border: 1px solid #999; + padding-left:5px; + margin-right:5px; +} + +.signin_form input[type="text"], .signin_form input[type="password"] { + padding-top:4px; /* balance of alignment between firefox/safari and IE */ } .signin_form .icon_input { padding-left: 20px; } +.signin_form #openid_identifier { + padding-left: 18px; +} + .or_label { margin-top: 20px; margin-bottom: 10px; -} \ No newline at end of file +} diff --git a/forum/skins/default/media/style/style.css b/forum/skins/default/media/style/style.css index dee175b..9932037 100644 --- a/forum/skins/default/media/style/style.css +++ b/forum/skins/default/media/style/style.css @@ -663,8 +663,6 @@ ul.errorlist li { margin-top: 5px; } -div.comments-container { padding: 0; } - .answer { border-bottom: 1px solid #CCCCCE; padding-top: 10px; @@ -677,13 +675,13 @@ div.comments-container { padding: 0; } min-height: 80px; } +.answered-by-owner { background: none repeat scroll 0 0 #E9E9FF; } + .accepted-answer { background-color: #EBFFE6; border-bottom-color: #9BD59B; } -.accepted-answer .comments-container { background-color: #CCFFBF; } - .answered { background: none repeat scroll 0 0 #E5EBF8; color: #314362; @@ -699,9 +697,6 @@ div.comments-container { padding: 0; } color: #6B2B28; } -.answered-by-owner { background: none repeat scroll 0 0 #E9E9FF; } -.answered-by-owner .comments-container { background-color: #E6ECFF; } - .tagsList { list-style-type: none; margin: 0; @@ -1111,7 +1106,6 @@ ul.form-horizontal-rows li input { } .post-controls { - float: left; font-size: 11px; line-height: 12px; margin-bottom: 5px; @@ -1120,11 +1114,6 @@ ul.form-horizontal-rows li input { #question-controls .tags { margin: 0 0 3px; } -.post-update-info-container { - float: right; - min-width: 190px; -} - .post-update-info { display: inline-block; float: right; @@ -1145,21 +1134,6 @@ ul.form-horizontal-rows li input { width: 32px; } -.comments-container { clear: both; } - -.admin { - background-color: #FFF380; - border: 1px solid darkred; - padding: 0 5px; -} - -.admin p { margin-bottom: 3px; } - -.admin #action_status { - font-weight: bold; - text-align: center; -} - #tagSelector { padding-bottom: 2px; } #hideIgnoredTagsControl { margin: 5px 0 0; } #hideIgnoredTagsCb { margin: 0 2px 0 1px; } @@ -1192,10 +1166,10 @@ a.post-vote.down.on,a.post-vote.down:hover { background: url("../images/vote-arr a.accept-answer { background: url("../images/vote-accepted.png") no-repeat scroll center center transparent; } a.accept-answer.on,a.accept-answer:hover { background: url("../images/vote-accepted-on.png") no-repeat scroll center center transparent; } -.post-score { +.post-score, .comments-char-left-count { color: #777777; font-family: Arial; - font-size: 140%; + font-size: 165%; font-weight: bold; padding: 0 0 3px; } @@ -1211,10 +1185,15 @@ a.accept-answer.on,a.accept-answer:hover { background: url("../images/vote-accep padding: 0; } +.comments-container { clear: both; } +.comments-container { padding: 0; } +.answered-by-owner .comments-container { background-color: #E6ECFF; } +.accepted-answer .comments-container { background-color: #CCFFBF; } + .comment { border-top: 1px dotted #CCCCCE; margin: 0; - width: 100%; + position: relative; } .comment.not_top_scorer { display: none; } @@ -1226,27 +1205,39 @@ a.accept-answer.on,a.accept-answer:hover { background: url("../images/vote-accep font-weight: bold; padding-top: 3px; vertical-align: top; + float: left; width: 22px; + height: 100%; + text-align: center; } .comment-text { color: #444444; - font-size: 11px; + font-size: 12px; margin: 0 0 0 22px; padding: 0; } +.comment-text p { + font-size: 12px; +} + .comment-info { font-size: 11px; - margin: 0; - text-align: right; + margin: 0 0 4px 0; + text-align: right; + height: 18px; + vertical-align: middle; } -a.comment-like,a.comment-delete,a.comment-edit { - float: right; +.comment-info * { + float: right; height: 18px; + margin-left: 4px; +} + +a.comment-like,a.comment-delete,a.comment-edit { margin-left: 2px; - position: relative; width: 18px; } @@ -1265,8 +1256,9 @@ a.comment-edit:hover { background: url("../images/comment-edit-hover.png") no-re .comment-form-widgets-container input { vertical-align: top; } .comment-form-widgets-container textarea { - height: 6em; + height: 80px; width: 80%; + float: left; } span.comment-chars-left { @@ -1312,3 +1304,69 @@ div.comment-tools a:hover { } .action-link-separator { color: #CCCCCC; } + +.deleted {background-color: #F4E7E7;} + +#command-loader { + position: fixed; + bottom: 0px; + left: 0px; + width: 24px; + height: 24px; + background: url('/m/default/media/images/indicator.gif') +} + +#command-loader.success { + background: url('/m/default/media/images/vote-accepted-on.png') +} + +.user-prompt { + width: 300px; +} + +.user-prompt select, .user-prompt textarea { + width: 100%; + padding: 0; + border: 0; +} + +.user-prompt .prompt-buttons { + text-align: right; +} + +.comment-form-buttons { + width: 18%; + height: 100%; + float: right; +} + +.comment-form-buttons input, .user-prompt .prompt-buttons button { + height: 16px; + line-height: 12px; + font-size: 110%; + border: 1px solid #999; +} + +.comment-form-buttons input { + width: 100%; + height: 22px; + vertical-align: center; + margin-top: 6px; +} + +.comments-char-left-count.warn { + color: orange; +} + +.moderation { + background-color: #FFF380; + border: 1px solid darkred; + padding: 0 5px; +} + +.moderation p { margin-bottom: 3px; } + +.moderation #action_status { + font-weight: bold; + text-align: center; +} \ No newline at end of file diff --git a/forum/skins/default/templates/ask.html b/forum/skins/default/templates/ask.html index a925e9d..35a6c2c 100644 --- a/forum/skins/default/templates/ask.html +++ b/forum/skins/default/templates/ask.html @@ -25,7 +25,7 @@ $('#pre-collapse').text(txt); }); - // Tags autocomplete action + //Tags autocomplete action $("#id_tags").autocomplete("/matching_tags", { minChars: 1, matchContains: true, diff --git a/forum/skins/default/templates/auth/signin.html b/forum/skins/default/templates/auth/signin.html index 798faeb..6f653f8 100644 --- a/forum/skins/default/templates/auth/signin.html +++ b/forum/skins/default/templates/auth/signin.html @@ -36,7 +36,10 @@

{% trans 'Or...' %}

{% endif %}
- {% trans "Click to sign in through any of these services." %} + {% blocktrans %} + External login services use OpenID technology, where your password always stays confidential between + you and your login provider and you don't have to remember another one. + {% endblocktrans %}
{% if request.user.is_anonymous %}
@@ -47,34 +50,38 @@
{% for provider in bigicon_providers %} {% endfor %}
{% for provider in smallicon_providers %} {% endfor %}
@@ -90,9 +97,11 @@ {% endfor %}

{% trans 'Or...' %}

-
- {% trans 'Click' %} here {% trans "if you're having troubles signing in." %} -
+
diff --git a/forum/skins/default/templates/node/post_controls.html b/forum/skins/default/templates/node/post_controls.html index 5b5fc86..c24660b 100644 --- a/forum/skins/default/templates/node/post_controls.html +++ b/forum/skins/default/templates/node/post_controls.html @@ -1,7 +1,7 @@ {% spaceless %} {% for control in controls %} - {{ control.text }} {% ifnotequal controls|last control %} diff --git a/forum/skins/default/templates/node/report.html b/forum/skins/default/templates/node/report.html new file mode 100644 index 0000000..dbc8630 --- /dev/null +++ b/forum/skins/default/templates/node/report.html @@ -0,0 +1,19 @@ +{% load i18n %} + +
+ {% trans "Please select a reason bellow or use the text box to input your own reason." %} + + +
+ +
+
+ \ No newline at end of file diff --git a/forum/skins/default/templates/osqaadmin/base.html b/forum/skins/default/templates/osqaadmin/base.html index e35a705..2acdc15 100644 --- a/forum/skins/default/templates/osqaadmin/base.html +++ b/forum/skins/default/templates/osqaadmin/base.html @@ -5,6 +5,7 @@ {% block forejs %} + {% block adminjs %}{% endblock %} {% endblock %} @@ -23,12 +24,12 @@ {% block sidebar %}
-

{% trans "Administration menu" %}

- +

{% trans "Administration menu" %}

+
{% if markdown %} @@ -65,5 +66,4 @@ {% endif %} {% endblock %} - - + diff --git a/forum/skins/default/templates/osqaadmin/index.html b/forum/skins/default/templates/osqaadmin/index.html index 9e35a35..64670f5 100644 --- a/forum/skins/default/templates/osqaadmin/index.html +++ b/forum/skins/default/templates/osqaadmin/index.html @@ -65,7 +65,7 @@

{% trans "Recent activity" %}

{% for activity in recent_activity %} - {% activity_item activity %} + {% activity_item activity request.user %} {% endfor %}
diff --git a/forum/skins/default/templates/post_contributor_info.html b/forum/skins/default/templates/post_contributor_info.html index 9997be5..2770644 100644 --- a/forum/skins/default/templates/post_contributor_info.html +++ b/forum/skins/default/templates/post_contributor_info.html @@ -35,20 +35,20 @@ {% get_score_badge post.author %}

{% endif %} {% else %} - {% if post.last_edited_at %} + {% if post.last_edited %}

{% ifequal post_type 'question' %} {% else %} {% endifequal %} - {% trans "updated" %} {% diff_date post.last_edited_at %} + {% trans "updated" %} {% diff_date post.last_edited.at %}

- {% if post.author != post.last_edited_by or wiki %} - {% gravatar post.last_edited_by 32 %} -

{{post.last_edited_by.get_profile_link}}
- {% get_score_badge post.last_edited_by %}

+ {% if post.author != post.last_edited.by or wiki %} + {% gravatar post.last_edited.by 32 %} +

{{post.last_edited.by.get_profile_link}}
+ {% get_score_badge post.last_edited.by %}

{% endif %} {% endif %} {% endifequal %} diff --git a/forum/skins/default/templates/question.html b/forum/skins/default/templates/question.html index 7a265c6..b6300d5 100644 --- a/forum/skins/default/templates/question.html +++ b/forum/skins/default/templates/question.html @@ -7,7 +7,7 @@ {% load humanize %} {% load i18n %} {% load cache %} -{% block title %}{% spaceless %}{{ question.get_question_title }}{% endspaceless %}{% endblock %} +{% block title %}{% spaceless %}{{ question.headline }}{% endspaceless %}{% endblock %} {% block forejs %} @@ -19,8 +19,6 @@ {% endif %} + + + + +