From 2841a5b8bd78f01c81143f889220fafd90784949 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Mon, 9 Apr 2012 22:57:21 -0500 Subject: added response by email when post was added to the moderation queue, also comments can be now moderated --- .../0118_auto__add_field_postrevision_by_email.py | 304 +++++++++++++++++++++ askbot/models/__init__.py | 62 ++++- askbot/models/post.py | 219 ++++++++++----- askbot/models/question.py | 14 +- askbot/skins/common/media/js/user.js | 25 +- askbot/skins/default/templates/macros.html | 2 +- 6 files changed, 529 insertions(+), 97 deletions(-) create mode 100644 askbot/migrations/0118_auto__add_field_postrevision_by_email.py diff --git a/askbot/migrations/0118_auto__add_field_postrevision_by_email.py b/askbot/migrations/0118_auto__add_field_postrevision_by_email.py new file mode 100644 index 00000000..7c304d1e --- /dev/null +++ b/askbot/migrations/0118_auto__add_field_postrevision_by_email.py @@ -0,0 +1,304 @@ +# -*- coding: 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 'PostRevision.by_email' + db.add_column('askbot_postrevision', 'by_email', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + def backwards(self, orm): + # Deleting field 'PostRevision.by_email' + db.delete_column('askbot_postrevision', 'by_email') + + models = { + 'askbot.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'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True'}), + 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}), + 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'askbot.activityauditstatus': { + 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'}, + 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'askbot.anonymousanswer': { + 'Meta': {'object_name': 'AnonymousAnswer'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Post']"}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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['askbot.BadgeData']"}), + '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'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"}) + }, + 'askbot.badgedata': { + 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}) + }, + 'askbot.emailfeedsetting': { + 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"}) + }, + 'askbot.favoritequestion': { + 'Meta': {'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'}), + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}) + }, + 'askbot.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['askbot.Tag']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"}) + }, + 'askbot.post': { + 'Meta': {'object_name': 'Post'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_posts'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}), + 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'askbot.postrevision': { + 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'}, + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}), + 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '125', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + 'askbot.questionview': { + 'Meta': {'object_name': 'QuestionView'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}), + 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"}) + }, + 'askbot.replyaddress': { + 'Meta': {'object_name': 'ReplyAddress'}, + 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}), + 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'to': "orm['askbot.Post']"}), + 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'askbot.repute': { + 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"}, + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + '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', [], {'to': "orm['auth.User']"}) + }, + 'askbot.tag': { + 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"}, + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + '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['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'askbot.thread': { + 'Meta': {'object_name': 'Thread'}, + 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), + 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", 'symmetrical': 'False', 'through': "orm['askbot.FavoriteQuestion']", 'to': "orm['auth.User']"}), + 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}), + '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': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'askbot.vote': { + 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}), + 'vote': ('django.db.models.fields.SmallIntegerField', [], {}), + 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"}) + }, + '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']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", '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'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + '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'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", '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'}) + } + } + + complete_apps = ['askbot'] \ No newline at end of file diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 7067ef94..3774bc7d 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -988,6 +988,7 @@ def user_post_comment( parent_post = None, body_text = None, timestamp = None, + by_email = False ): """post a comment on behalf of the user to parent_post @@ -1006,9 +1007,11 @@ def user_post_comment( user = self, comment = body_text, added_at = timestamp, + by_email = by_email ) parent_post.thread.invalidate_cached_data() - award_badges_signal.send(None, + award_badges_signal.send( + None, event = 'post_comment', actor = self, context_object = comment, @@ -1333,7 +1336,8 @@ def user_post_question( tags = None, wiki = False, is_anonymous = False, - timestamp = None + timestamp = None, + by_email = False ): """makes an assertion whether user can post the question then posts it and returns the question object""" @@ -1350,6 +1354,8 @@ def user_post_question( if timestamp is None: timestamp = datetime.datetime.now() + #todo: split this into "create thread" + "add queston", if text exists + #or maybe just add a blank question post anyway thread = Thread.objects.create_new( author = self, title = title, @@ -1358,6 +1364,7 @@ def user_post_question( added_at = timestamp, wiki = wiki, is_anonymous = is_anonymous, + by_email = by_email ) question = thread._question_post() if question.author != self: @@ -1366,42 +1373,60 @@ def user_post_question( # because they set some attributes for that instance and expect them to be changed also for question.author return question -def user_edit_comment(self, comment_post=None, body_text = None): +@auto_now_timestamp +def user_edit_comment( + self, + comment_post=None, + body_text = None, + timestamp = None, + by_email = False + ): """apply edit to a comment, the method does not change the comments timestamp and no signals are sent todo: see how this can be merged with edit_post todo: add timestamp """ self.assert_can_edit_comment(comment_post) - comment_post.text = body_text - comment_post.parse_and_save(author = self) - comment_post.thread.invalidate_cached_data() + comment_post.apply_edit( + text = body_text, + edited_at = timestamp, + edited_by = self, + by_email = by_email + ) def user_edit_post(self, post = None, body_text = None, revision_comment = None, - timestamp = None): + timestamp = None, + by_email = False + ): """a simple method that edits post body todo: unify it in the style of just a generic post this requires refactoring of underlying functions because we cannot bypass the permissions checks set within """ if post.post_type == 'comment': - self.edit_comment(comment_post = post, body_text = body_text) + self.edit_comment( + comment_post = post, + body_text = body_text, + by_email = by_email + ) elif post.post_type == 'answer': self.edit_answer( answer = post, body_text = body_text, timestamp = timestamp, - revision_comment = revision_comment + revision_comment = revision_comment, + by_email = by_email ) elif post.post_type == 'question': self.edit_question( question = post, body_text = body_text, timestamp = timestamp, - revision_comment = revision_comment + revision_comment = revision_comment, + by_email = by_email ) elif post.post_type == 'tag_wiki': post.apply_edit( @@ -1411,6 +1436,7 @@ def user_edit_post(self, #todo: summary name clash in question and question revision comment = revision_comment, wiki = True, + by_email = False ) else: raise NotImplementedError() @@ -1427,9 +1453,11 @@ def user_edit_question( edit_anonymously = False, timestamp = None, force = False,#if True - bypass the assert + by_email = False ): if force == False: self.assert_can_edit_question(question) + question.apply_edit( edited_at = timestamp, edited_by = self, @@ -1440,8 +1468,11 @@ def user_edit_question( tags = tags, wiki = wiki, edit_anonymously = edit_anonymously, + by_email = by_email ) + question.thread.invalidate_cached_data() + award_badges_signal.send(None, event = 'edit_question', actor = self, @@ -1457,7 +1488,8 @@ def user_edit_answer( revision_comment = None, wiki = False, timestamp = None, - force = False#if True - bypass the assert + force = False,#if True - bypass the assert + by_email = False ): if force == False: self.assert_can_edit_answer(answer) @@ -1467,6 +1499,7 @@ def user_edit_answer( text = body_text, comment = revision_comment, wiki = wiki, + by_email = by_email ) answer.thread.invalidate_cached_data() award_badges_signal.send(None, @@ -1482,7 +1515,8 @@ def user_post_answer( body_text = None, follow = False, wiki = False, - timestamp = None + timestamp = None, + by_email = False ): #todo: move this to assertion - user_assert_can_post_answer @@ -1494,6 +1528,7 @@ def user_post_answer( now = datetime.datetime.now() asked = question.added_at + #todo: this is an assertion, must be moved out if (now - asked < delta and self.reputation < askbot_settings.MIN_REP_TO_ANSWER_OWN_QUESTION): diff = asked + delta - now days = diff.days @@ -1544,7 +1579,8 @@ def user_post_answer( text = body_text, added_at = timestamp, email_notify = follow, - wiki = wiki + wiki = wiki, + by_email = by_email ) answer_post.thread.invalidate_cached_data() award_badges_signal.send(None, diff --git a/askbot/models/post.py b/askbot/models/post.py index 44ce5728..e6e7ec9d 100644 --- a/askbot/models/post.py +++ b/askbot/models/post.py @@ -158,20 +158,33 @@ class PostManager(BaseQuerySetManager): post_type = 'tag_wiki' ) - def create_new(self, thread, author, added_at, text, wiki=False, email_notify=False, post_type=None): + def create_new( + self, + thread, + author, + added_at, + text, + parent = None, + wiki = False, + email_notify = False, + post_type = None, + by_email = False + ): # TODO: Some of this code will go to Post.objects.create_new assert(post_type in const.POST_TYPES) post = Post( - post_type=post_type, - thread=thread, - author=author, - added_at=added_at, - wiki=wiki, - text=text, + post_type = post_type, + thread = thread, + parent = parent, + author = author, + added_at = added_at, + wiki = wiki, + text = text, #.html field is denormalized by the save() call ) + if post.wiki: post.last_edited_by = post.author post.last_edited_at = added_at @@ -180,22 +193,34 @@ class PostManager(BaseQuerySetManager): post.parse_and_save(author=author) post.add_revision( - author=author, - revised_at=added_at, - text=text, + author = author, + revised_at = added_at, + text = text, comment = const.POST_STATUS['default_version'], + by_email = by_email ) return post - def create_new_answer(self, thread, author, added_at, text, wiki=False, email_notify=False): + #todo: instead of this, have Thread.add_answer() + def create_new_answer( + self, + thread, + author, + added_at, + text, + wiki = False, + email_notify = False, + by_email = False + ): answer = self.create_new( thread, author, added_at, text, wiki = wiki, - post_type = 'answer' + post_type = 'answer', + by_email = by_email ) #set notification/delete if email_notify: @@ -204,7 +229,8 @@ class PostManager(BaseQuerySetManager): thread.followed_by.remove(author) #update thread data - thread.answer_count +=1 + #todo: this totally belongs to some `Thread` class method + thread.answer_count += 1 thread.save() thread.set_last_activity(last_activity_at=added_at, last_activity_by=author) # this should be here because it regenerates cached thread summary html return answer @@ -676,22 +702,27 @@ class Post(models.Model): self._cached_comments = list() return self._cached_comments - def add_comment(self, comment=None, user=None, added_at=None): + def add_comment( + self, + comment=None, + user=None, + added_at=None, + by_email = False): + if added_at is None: added_at = datetime.datetime.now() - if None in (comment ,user): + if None in (comment, user): raise Exception('arguments comment and user are required') - from askbot.models import Post - comment = Post( - post_type='comment', - thread=self.thread, - parent=self, - text=comment, - author=user, - added_at=added_at - ) - comment.parse_and_save(author = user) + comment_post = self.__class__.objects.create_new( + self.thread, + user, + added_at, + comment, + parent = self, + post_type = 'comment', + by_email = by_email + ) self.comment_count = self.comment_count + 1 self.save() @@ -711,7 +742,7 @@ class Post(models.Model): # origin_post.last_activity_by = user # origin_post.save() - return comment + return comment_post def get_global_tag_based_subscribers( self, @@ -1298,7 +1329,16 @@ class Post(models.Model): def get_tag_names(self): return self.thread.get_tag_names() - def __apply_edit(self, edited_at=None, edited_by=None, text=None, comment=None, wiki=False): + def __apply_edit( + self, + edited_at = None, + edited_by = None, + text = None, + comment = None, + wiki = False, + edit_anonymously = False, + by_email = False + ): if text is None: text = self.get_latest_revision().text if edited_at is None: @@ -1310,26 +1350,40 @@ class Post(models.Model): self.last_edited_by = edited_by #self.html is denormalized in save() self.text = text - #todo: bug wiki has no effect here + self.is_anonymous = edit_anonymously + + #wiki is an eternal trap whence there is no exit + if self.wiki == False and wiki == True: + self.wiki = True #must add revision before saving the answer self.add_revision( author = edited_by, revised_at = edited_at, text = text, - comment = comment + comment = comment, + by_email = by_email ) self.parse_and_save(author = edited_by) - def _answer__apply_edit(self, edited_at=None, edited_by=None, text=None, comment=None, wiki=False): + def _answer__apply_edit( + self, + edited_at = None, + edited_by = None, + text = None, + comment = None, + wiki = False, + by_email = False + ): self.__apply_edit( edited_at = edited_at, edited_by = edited_by, text = text, comment = comment, - wiki = wiki + wiki = wiki, + by_email = by_email ) if edited_at is None: edited_at = datetime.datetime.now() @@ -1337,33 +1391,22 @@ class Post(models.Model): def _question__apply_edit(self, edited_at=None, edited_by=None, title=None,\ text=None, comment=None, tags=None, wiki=False,\ - edit_anonymously = False): + edit_anonymously = False, + by_email = False + ): + #todo: the thread editing should happen outside of this + #method, then we'll be able to unify all the *__apply_edit + #methods latest_revision = self.get_latest_revision() #a hack to allow partial edits - important for SE loader if title is None: title = self.thread.title - if text is None: - text = latest_revision.text if tags is None: tags = latest_revision.tagnames - - if edited_by is None: - raise Exception('parameter edited_by is required') - if edited_at is None: edited_at = datetime.datetime.now() - # Update the Question itself - self.last_edited_at = edited_at - self.last_edited_by = edited_by - self.text = text - self.is_anonymous = edit_anonymously - - #wiki is an eternal trap whence there is no exit - if self.wiki == False and wiki == True: - self.wiki = True - # Update the Question tag associations if latest_revision.tagnames != tags: self.thread.update_tags(tagnames = tags, user = edited_by, timestamp = edited_at) @@ -1372,29 +1415,39 @@ class Post(models.Model): self.thread.tagnames = tags self.thread.save() - # Create a new revision - self.add_revision( # has to be called AFTER updating the thread, otherwise it doesn't see new tags and the new title - author = edited_by, + self.__apply_edit( + edited_at = edited_at, + edited_by = edited_by, text = text, - revised_at = edited_at, - is_anonymous = edit_anonymously, comment = comment, + wiki = wiki, + edit_anonymously = edit_anonymously, + by_email = by_email ) - self.parse_and_save(author = edited_by) - self.thread.set_last_activity(last_activity_at=edited_at, last_activity_by=edited_by) def apply_edit(self, *args, **kwargs): + #todo: unify this, here we have unnecessary indirection + #the question__apply_edit function is backwards: + #the title edit and tag edit should apply to thread + #not the question post if self.is_answer(): return self._answer__apply_edit(*args, **kwargs) elif self.is_question(): return self._question__apply_edit(*args, **kwargs) - elif self.is_tag_wiki(): + elif self.is_tag_wiki() or self.is_comment(): return self.__apply_edit(*args, **kwargs) raise NotImplementedError - def __add_revision(self, author=None, revised_at=None, text=None, comment=None): + def __add_revision( + self, + author = None, + revised_at = None, + text = None, + comment = None, + by_email = False + ): #todo: this may be identical to Question.add_revision if None in (author, revised_at, text): raise Exception('arguments author, revised_at and text are required') @@ -1406,12 +1459,13 @@ class Post(models.Model): comment = 'No.%s Revision' % rev_no from askbot.models.post import PostRevision return PostRevision.objects.create_answer_revision( - post=self, - author=author, - revised_at=revised_at, - text=text, - summary=comment, - revision=rev_no + post = self, + author = author, + revised_at = revised_at, + text = text, + summary = comment, + revision = rev_no, + by_email = by_email ) def _question__add_revision( @@ -1420,7 +1474,8 @@ class Post(models.Model): is_anonymous = False, text = None, comment = None, - revised_at = None + revised_at = None, + by_email = False ): if None in (author, text): raise Exception('author, text and comment are required arguments') @@ -1441,11 +1496,13 @@ class Post(models.Model): revised_at = revised_at, tagnames = self.thread.tagnames, summary = comment, - text = text + text = text, + by_email = by_email ) def add_revision(self, *kargs, **kwargs): - if self.post_type in ('answer', 'tag_wiki'): + #todo: unify these + if self.post_type in ('answer', 'comment', 'tag_wiki'): return self.__add_revision(*kargs, **kwargs) elif self.is_question(): return self._question__add_revision(*kargs, **kwargs) @@ -1628,6 +1685,8 @@ class PostRevision(models.Model): post = models.ForeignKey('askbot.Post', related_name='revisions', null=True, blank=True) + #todo: remove this field, as revision type is determined by the + #Post.post_type revision_type is a useless field revision_type = models.SmallIntegerField(choices=REVISION_TYPE_CHOICES) # TODO: remove as we have Post now revision = models.PositiveIntegerField() @@ -1640,6 +1699,8 @@ class PostRevision(models.Model): approved_by = models.ForeignKey(User, null = True, blank = True) approved_at = models.DateTimeField(null= True, blank = True) + by_email = models.BooleanField(default=False)#true, if edited by email + # Question-specific fields title = models.CharField(max_length=300, blank=True, default='') tagnames = models.CharField(max_length=125, blank=True, default='') @@ -1691,11 +1752,27 @@ class PostRevision(models.Model): self.post.thread.approved = False self.post.thread.save() #above changes will hide post from the public display - message = _( - 'Your post was placed on the moderation queue ' - 'and will be published after the moderator approval.' - ) - self.author.message_set.create(message = message) + if self.by_email: + from askbot.utils.mail import send_mail + email_context = { + 'site': askbot_settings.APP_SHORT_NAME + } + body_text = _( + 'Thank you for your post to %(site)s. ' + 'It will be published after the moderators review.' + ) % email_context + send_mail( + subject_line = _('your post to %(site)s') % email_context, + body_text = body_text, + recipient_list = [self.author.email,], + ) + + else: + message = _( + 'Your post was placed on the moderation queue ' + 'and will be published after the moderator approval.' + ) + self.author.message_set.create(message = message) else: #In this case, for now we just flag the edit #for the moderators. diff --git a/askbot/models/question.py b/askbot/models/question.py index 51879035..21cf21d0 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -62,7 +62,17 @@ class ThreadManager(models.Manager): def create(self, *args, **kwargs): raise NotImplementedError - def create_new(self, title, author, added_at, wiki, text, tagnames=None, is_anonymous=False): + def create_new( + self, + title, + author, + added_at, + wiki, + text, + tagnames = None, + is_anonymous = False, + by_email = False + ): # TODO: Some of this code will go to Post.objects.create_new thread = super( @@ -75,6 +85,7 @@ class ThreadManager(models.Manager): last_activity_by=author ) + #todo: code below looks like ``Post.objects.create_new()`` question = Post( post_type='question', thread=thread, @@ -103,6 +114,7 @@ class ThreadManager(models.Manager): text = text, comment = const.POST_STATUS['default_version'], revised_at = added_at, + by_email = by_email ) # INFO: Question has to be saved before update_tags() is called diff --git a/askbot/skins/common/media/js/user.js b/askbot/skins/common/media/js/user.js index c0e870b2..24ca060f 100644 --- a/askbot/skins/common/media/js/user.js +++ b/askbot/skins/common/media/js/user.js @@ -69,8 +69,11 @@ $(document).ready(function(){ } } if (action_type == 'remove_flag'){ - msg = ngettext('Remove all flags on this entry?', - 'Remove all flags on these entries?', data['id_list'].length); + msg = ngettext( + 'Remove all flags and approve this entry?', + 'Remove all flags and approve these entries?', + data['id_list'].length + ); if (confirm(msg) === false){ return; } @@ -117,15 +120,15 @@ $(document).ready(function(){ } ); - setupButtonEventHandlers($('.re_expand'), - function(e){ - e.preventDefault(); - var re_snippet = $(this).find(".re_snippet:first") - var re_content = $(this).find(".re_content:first") - $(re_snippet).slideToggle(); - $(re_content).slideToggle(); - } - ); + //setupButtonEventHandlers($('.re_expand'), + // function(e){ + // e.preventDefault(); + // var re_snippet = $(this).find(".re_snippet:first") + // var re_content = $(this).find(".re_content:first") + // $(re_snippet).slideToggle(); + // $(re_content).slideToggle(); + // } + //); $('.badge-context-toggle').each(function(idx, elem){ var context_list = $(elem).parent().next('ul'); diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index af04da72..d4871f44 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -39,7 +39,7 @@ {% if inbox_section == 'flags' %} -
{{ response.response_snippet }}
+
{{ response.response_content }}
{% endif %} -- cgit v1.2.3-1-g7c22