From 8c1f61bcd212363720f11a65e4e43bc96a9987f7 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Mon, 14 Feb 2011 04:51:57 -0500 Subject: almost finished anonymous posting feature still need to fix presentational parts and make sure that rep cannot accumulate when posts are made anonymously --- askbot/conf/forum_data_rules.py | 14 + askbot/forms.py | 151 +++++++++- ...uestion_is_anonymous__add_field_questionrevi.py | 319 +++++++++++++++++++++ askbot/models/__init__.py | 18 +- askbot/models/question.py | 68 ++++- askbot/skins/default/media/style/style.css | 6 + askbot/skins/default/templates/answer_edit.html | 5 +- .../skins/default/templates/blocks/ask_form.html | 10 +- askbot/skins/default/templates/macros.html | 22 +- askbot/skins/default/templates/question.html | 7 +- askbot/skins/default/templates/question_edit.html | 15 +- askbot/tests/db_api_tests.py | 32 +++ askbot/tests/form_tests.py | 114 ++++++++ askbot/tests/utils.py | 7 + askbot/views/writers.py | 82 ++++-- 15 files changed, 807 insertions(+), 63 deletions(-) create mode 100644 askbot/migrations/0036_auto__add_field_anonymousquestion_is_anonymous__add_field_questionrevi.py diff --git a/askbot/conf/forum_data_rules.py b/askbot/conf/forum_data_rules.py index 02e5b0a3..7dfee665 100644 --- a/askbot/conf/forum_data_rules.py +++ b/askbot/conf/forum_data_rules.py @@ -20,6 +20,20 @@ settings.register( ) ) +settings.register( + livesettings.BooleanValue( + FORUM_DATA_RULES, + 'ALLOW_ASK_ANONYMOUSLY', + default=True, + description=_('Allow asking questions anonymously'), + help_text=_( + 'Users do not accrue reputation for anonymous questions ' + 'and their identity is not revealed until they change their ' + 'mind' + ) + ) +) + settings.register( livesettings.IntegerValue( FORUM_DATA_RULES, diff --git a/askbot/forms.py b/askbot/forms.py index 115a7485..0f6edef4 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -44,7 +44,6 @@ def filter_choices(remove_choices = None, from_choices = None): return filtered_choices - COUNTRY_CHOICES = (('unknown',_('select country')),) + countries.COUNTRIES class CountryField(forms.ChoiceField): @@ -461,16 +460,64 @@ class FeedbackForm(forms.Form): message = forms.CharField(label=_('Your message:'), max_length=800,widget=forms.Textarea(attrs={'cols':60})) next = NextUrlField() -class AskForm(forms.Form): +class FormWithHideableFields(object): + """allows to swap a field widget to HiddenInput() and back""" + + def hide_field(self, name): + """replace widget with HiddenInput() + and save the original in the __hidden_fields dictionary + """ + if not hasattr(self, '__hidden_fields'): + self.__hidden_fields = dict() + if name in self.__hidden_fields: + return + self.__hidden_fields[name] = self.fields[name].widget + self.fields[name].widget = forms.HiddenInput() + + def show_field(self, name): + """restore the original widget on the field + if it was previously hidden + """ + if name in self.__hidden_fields: + self.fields[name] = self.__hidden_fields.pop(name) + +class AskForm(forms.Form, FormWithHideableFields): + """the form used to askbot questions + field ask_anonymously is shown to the user if the + if ALLOW_ASK_ANONYMOUSLY live setting is True + however, for simplicity, the value will always be present + in the cleaned data, and will evaluate to False if the + settings forbids anonymous asking + """ title = TitleField() text = EditorField() tags = TagNamesField() wiki = WikiField() - + ask_anonymously = forms.BooleanField( + label = _('Ask anonymously'), + help_text = _( + 'Check if you do not want to reveal your name ' + 'when asking this question' + ), + required = False, + ) openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'})) user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35})) email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35})) + def __init__(self, *args, **kwargs): + super(AskForm, self).__init__(*args, **kwargs) + #hide ask_anonymously field + if askbot_settings.ALLOW_ASK_ANONYMOUSLY == False: + self.hide_field('ask_anonymously') + + def clean_ask_anonymously(self): + """returns false if anonymous asking is not allowed + """ + if askbot_settings.ALLOW_ASK_ANONYMOUSLY == False: + self.cleaned_data['ask_anonymously'] = False + return self.cleaned_data['ask_anonymously'] + class AnswerForm(forms.Form): text = EditorField() wiki = WikiField() @@ -516,21 +563,113 @@ class RevisionForm(forms.Form): for r in revisions] self.fields['revision'].initial = latest_revision.revision -class EditQuestionForm(forms.Form): +class EditQuestionForm(forms.Form, FormWithHideableFields): title = TitleField() text = EditorField() tags = TagNamesField() summary = SummaryField() wiki = WikiField() + reveal_identity = forms.BooleanField( + help_text = _( + 'You have asked this question anonymously, ' + 'if you decide to reveal your identity, please check ' + 'this box.' + ), + label = _('Reveal identity'), + required = False, + ) #todo: this is odd that this form takes question as an argument - def __init__(self, question, revision, *args, **kwargs): + def __init__(self, *args, **kwargs): """populate EditQuestionForm with initial data""" + self.question = kwargs.pop('question') + self.user = kwargs.pop('user') + revision = kwargs.pop('revision') super(EditQuestionForm, self).__init__(*args, **kwargs) self.fields['title'].initial = revision.title self.fields['text'].initial = revision.text self.fields['tags'].initial = revision.tagnames - self.fields['wiki'].initial = question.wiki + self.fields['wiki'].initial = self.question.wiki + #hide the reveal identity field + if not self.can_stay_anonymous(): + self.hide_field('reveal_identity') + + def can_stay_anonymous(self): + """determines if the user cat keep editing the question + anonymously""" + return (askbot_settings.ALLOW_ASK_ANONYMOUSLY \ + and self.question.is_anonymous \ + and self.user.is_owner_of(self.question) + ) + + def clean_reveal_identity(self): + """cleans the reveal_identity field + which determines whether previous anonymous + edits must be rewritten as not anonymous + this does not necessarily mean that the edit will be anonymous + + only does real work when question is anonymous + based on the following truth table: + + is_anon can owner checked cleaned data + - * * * False (ignore choice in checkbox) + + + + + True + + + + - False + + + - + Raise(Not owner) + + + - - False + + - + + True (setting "can" changed, say yes) + + - + - False, warn (but prev edits stay anon) + + - - + Raise(Not owner) + + - - - False + """ + value = self.cleaned_data['reveal_identity'] + if self.question.is_anonymous: + if value == True: + if self.user.is_owner_of(self.question): + #regardless of the ALLOW_ASK_ANONYMOUSLY + return True + else: + self.show_field('reveal_identity') + del self.cleaned_data['reveal_identity'] + raise forms.ValidationError( + _( + 'Sorry, only owner of the anonymous ' + 'question can reveal his or her ' + 'identity, please uncheck the ' + 'box' + ) + ) + else: + can_ask_anon = askbot_settings.ALLOW_ASK_ANONYMOUSLY + is_owner = self.user.is_owner_of(self.question) + if can_ask_anon == False and is_owner: + self.show_field('reveal_identity') + raise forms.ValidationError( + _( + 'Sorry, apparently rules have just changed - ' + 'it is no longer possible to ask anonymously. ' + 'Please either check the "reveal identity" box ' + 'or reload this page and try editing the question ' + 'again.' + ) + ) + return False + else: + #takes care of 8 possibilities - first row of the table + return False + + def clean(self): + """Purpose of this function is to determine whether + it is ok to apply edit anonymously in the synthetic + field edit_anonymously. It relies on correct cleaning + if the "reveal_identity" field + """ + reveal_identity = self.cleaned_data.get('reveal_identity', False) + stay_anonymous = False + if reveal_identity == False and self.can_stay_anonymous(): + stay_anonymous = True + self.cleaned_data['stay_anonymous'] = stay_anonymous + return self.cleaned_data class EditAnswerForm(forms.Form): text = EditorField() diff --git a/askbot/migrations/0036_auto__add_field_anonymousquestion_is_anonymous__add_field_questionrevi.py b/askbot/migrations/0036_auto__add_field_anonymousquestion_is_anonymous__add_field_questionrevi.py new file mode 100644 index 00000000..e515b402 --- /dev/null +++ b/askbot/migrations/0036_auto__add_field_anonymousquestion_is_anonymous__add_field_questionrevi.py @@ -0,0 +1,319 @@ +# 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 'AnonymousQuestion.is_anonymous' + db.add_column('askbot_anonymousquestion', 'is_anonymous', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + # Adding field 'QuestionRevision.is_anonymous' + db.add_column(u'question_revision', 'is_anonymous', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + # Adding field 'Question.is_anonymous' + db.add_column(u'question', 'is_anonymous', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'AnonymousQuestion.is_anonymous' + db.delete_column('askbot_anonymousquestion', 'is_anonymous') + + # Deleting field 'QuestionRevision.is_anonymous' + db.delete_column(u'question_revision', 'is_anonymous') + + # Deleting field 'Question.is_anonymous' + db.delete_column(u'question', 'is_anonymous') + + + 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', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True'}), + 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'to': "orm['auth.User']"}), + 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'through': "'ActivityAuditStatus'", 'to': "orm['auth.User']"}), + '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.Question']"}), + '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', 'blank': 'True'}) + }, + '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', 'blank': 'True'}), + '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', 'blank': 'True'}) + }, + 'askbot.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'}), + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + '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_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['askbot.Question']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + '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', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'askbot.answerrevision': { + 'Meta': {'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"}, + 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Answer']"}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}) + }, + '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', 'blank': 'True'}), + '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': {'object_name': 'BadgeData'}, + 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'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', 'db_index': 'True'}) + }, + 'askbot.comment': { + 'Meta': {'object_name': 'Comment', 'db_table': "u'comment'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"}) + }, + 'askbot.emailfeedsetting': { + 'Meta': {'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'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"}) + }, + '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.question': { + 'Meta': {'object_name': 'Question', 'db_table': "u'question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}), + '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['auth.User']"}), + 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + '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_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'through': "'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_questions'", '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', 'blank': 'True'}), + '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'", 'to': "orm['auth.User']"}), + '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_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}), + 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'to': "orm['askbot.Tag']"}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + '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', 'blank': 'True'}), + 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + 'askbot.questionrevision': { + 'Meta': {'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Question']"}), + 'revised_at': ('django.db.models.fields.DateTimeField', [], {}), + 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}) + }, + '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.Question']"}), + 'when': ('django.db.models.fields.DateTimeField', [], {}), + 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", '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.Question']", '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': {'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', '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['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}) + }, + 'askbot.vote': { + 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + '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'}) + }, + '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'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + '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'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', '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'}), + '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']", 'blank': 'True'}), + 'has_custom_avatar': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), + 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', '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'}), + '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', 'blank': 'True'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'tag_filter_setting': ('django.db.models.fields.CharField', [], {'default': "'ignored'", 'max_length': '16'}), + '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'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + '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'}) + } + } + + complete_apps = ['askbot'] diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index b4d9f4d4..260ad0c5 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -998,6 +998,7 @@ def user_post_question( body_text = None, tags = None, wiki = False, + is_anonymous = False, timestamp = None ): @@ -1018,7 +1019,8 @@ def user_post_question( text = body_text, tagnames = tags, added_at = timestamp, - wiki = wiki + wiki = wiki, + is_anonymous = is_anonymous, ) return question @@ -1039,7 +1041,8 @@ def user_edit_question( revision_comment = None, tags = None, wiki = False, - timestamp = None + edit_anonymously = False, + timestamp = None, ): self.assert_can_edit_question(question) question.apply_edit( @@ -1051,6 +1054,7 @@ def user_edit_question( comment = revision_comment, tags = tags, wiki = wiki, + edit_anonymously = edit_anonymously, ) award_badges_signal.send(None, event = 'edit_question', @@ -1224,6 +1228,15 @@ def user_is_watched(self): def user_is_approved(self): return (self.status == 'a') +def user_is_owner_of(self, obj): + """True if user owns object + False otherwise + """ + if isinstance(obj, Question): + return self == obj.author + else: + raise NotImplementedError() + def user_set_status(self, new_status): """sets new status to user @@ -1664,6 +1677,7 @@ User.add_to_class('is_approved', user_is_approved) User.add_to_class('is_watched', user_is_watched) User.add_to_class('is_suspended', user_is_suspended) User.add_to_class('is_blocked', user_is_blocked) +User.add_to_class('is_owner_of', user_is_owner_of) User.add_to_class('can_moderate_user', user_can_moderate_user) User.add_to_class('moderate_user_reputation', user_moderate_user_reputation) User.add_to_class('set_status', user_set_status) diff --git a/askbot/models/question.py b/askbot/models/question.py index 04147c92..f1612e88 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -37,7 +37,16 @@ QUESTION_ORDER_BY_MAP = { } class QuestionManager(models.Manager): - def create_new(self, title=None,author=None,added_at=None, wiki=False,tagnames=None, text=None): + def create_new( + self, + title = None, + author = None, + added_at = None, + wiki = False, + is_anonymous = False, + tagnames = None, + text = None + ): question = Question( title = title, @@ -46,6 +55,7 @@ class QuestionManager(models.Manager): last_activity_at = added_at, last_activity_by = author, wiki = wiki, + is_anonymous = is_anonymous, tagnames = tagnames, #html field is denormalized in .save() call text = text, @@ -64,10 +74,11 @@ class QuestionManager(models.Manager): question.update_tags(tagnames = tagnames, user = author, timestamp = added_at) question.add_revision( - author=author, - text=text, - comment=const.POST_STATUS['default_version'], - revised_at=added_at, + author = author, + is_anonymous = is_anonymous, + text = text, + comment = const.POST_STATUS['default_version'], + revised_at = added_at, ) return question @@ -312,6 +323,7 @@ class Question(content.Content, DeletableContent): summary = models.CharField(max_length=180) favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions') + is_anonymous = models.BooleanField(default=False) objects = QuestionManager() @@ -335,6 +347,17 @@ class Question(content.Content, DeletableContent): except django_exceptions.PermissionDenied: raise exceptions.QuestionHidden(message) + def remove_author_anonymity(self): + """removes anonymous flag from the question + and all its revisions + the function calls update method to make sure that + signals are not called + """ + #it is important that update method is called - not save, + #because we do not want the signals to fire here + Question.objects.filter(id = self.id).update(is_anonymous = False) + self.revisions.all().update(is_anonymous = False) + def update_answer_count(self, save = True): """updates the denormalized field 'answer_count' on the question @@ -565,7 +588,8 @@ class Question(content.Content, DeletableContent): return self def apply_edit(self, edited_at=None, edited_by=None, title=None,\ - text=None, comment=None, tags=None, wiki=False): + text=None, comment=None, tags=None, wiki=False, \ + edit_anonymously = False): latest_revision = self.get_latest_revision() #a hack to allow partial edits - important for SE loader @@ -590,6 +614,7 @@ class Question(content.Content, DeletableContent): self.last_activity_by = edited_by self.tagnames = tags 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: @@ -604,12 +629,20 @@ class Question(content.Content, DeletableContent): author = edited_by, text = text, revised_at = edited_at, + is_anonymous = edit_anonymously, comment = comment, ) self.parse_and_save(author = edited_by) - def add_revision(self,author=None, text=None, comment=None, revised_at=None): + def add_revision( + self, + author = None, + is_anonymous = False, + text = None, + comment = None, + revised_at = None + ): if None in (author, text, comment): raise Exception('author, text and comment are required arguments') rev_no = self.revisions.all().count() + 1 @@ -624,6 +657,7 @@ class Question(content.Content, DeletableContent): revision = rev_no, title = self.title, author = author, + is_anonymous = is_anonymous, revised_at = revised_at, tagnames = self.tagnames, summary = comment, @@ -779,6 +813,7 @@ class QuestionRevision(ContentRevision): question = models.ForeignKey(Question, related_name='revisions') title = models.CharField(max_length=300) tagnames = models.CharField(max_length=125) + is_anonymous = models.BooleanField(default=False) class Meta(ContentRevision.Meta): db_table = u'question_revision' @@ -812,17 +847,24 @@ class QuestionRevision(ContentRevision): return u'revision %s of %s' % (self.revision, self.title) class AnonymousQuestion(AnonymousContent): + """question that was asked before logging in + maybe the name is a little misleading, the user still + may or may not want to stay anonymous after the question + is published + """ title = models.CharField(max_length=300) tagnames = models.CharField(max_length=125) + is_anonymous = models.BooleanField(default=False) def publish(self,user): added_at = datetime.datetime.now() Question.objects.create_new( - title=self.title, - author=user, - added_at=added_at, - wiki=self.wiki, - tagnames=self.tagnames, - text=self.text, + title = self.title, + added_at = added_at, + author = user, + wiki = self.wiki, + is_anonymous = self.is_anonymous, + tagnames = self.tagnames, + text = self.text, ) self.delete() diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css index 97adc3e0..7a3ab944 100755 --- a/askbot/skins/default/media/style/style.css +++ b/askbot/skins/default/media/style/style.css @@ -1153,6 +1153,12 @@ span.form-error { margin-top: 10px; } +.checkbox { + margin-left:5px; + font-weight:normal; + cursor:help +} + .edit-content-html { border-top: 1px dotted #D8D2A9; border-bottom: 1px dotted #D8D2A9; diff --git a/askbot/skins/default/templates/answer_edit.html b/askbot/skins/default/templates/answer_edit.html index e67973b1..0dc137ae 100644 --- a/askbot/skins/default/templates/answer_edit.html +++ b/askbot/skins/default/templates/answer_edit.html @@ -17,11 +17,14 @@
{{ revision_form.revision }}
- {{macros.edit_post(form, settings.WIKI_ON and answer.wiki == False)}} + {{ macros.edit_post(form) }}
 
+ {% if settings.WIKI_ON and answer.wiki == False %} + {{ macros.checkbox_in_div(form.wiki) }} + {% endif %} diff --git a/askbot/skins/default/templates/blocks/ask_form.html b/askbot/skins/default/templates/blocks/ask_form.html index ff7fea0d..e1a93711 100644 --- a/askbot/skins/default/templates/blocks/ask_form.html +++ b/askbot/skins/default/templates/blocks/ask_form.html @@ -22,11 +22,19 @@ {{ form.title.help_text }} - {{macros.edit_post(form, settings.WIKI_ON, post_type='question', edit_title=False)}} + {{macros.edit_post(form, post_type='question', edit_title=False)}} {% if not request.user.is_authenticated() %} {% else %} {% endif %} +
+ {% if settings.WIKI_ON %} + {{ macros.checkbox_in_div(form.wiki) }} + {% endif %} + {% if settings.ALLOW_ASK_ANONYMOUSLY %} + {{ macros.checkbox_in_div(form.ask_anonymously) }} + {% endif %} +
diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index 32d4c5bb..84716dcf 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -431,7 +431,20 @@ poor design of the data or methods on data objects #} {%- endmacro %} -{%- macro edit_post(post_form, wiki_on, post_type=None, edit_title=False) -%} +{%- macro checkbox_in_div(checkbox_field, class = 'checkbox') -%} + + {{ checkbox_field }} + {{ checkbox_field.label_tag() }} + {{ checkbox_field.errors }} + +{%- endmacro -%} + +{%- macro edit_post( + post_form, + post_type=None, + edit_title=False) +-%} {% if edit_title %}

@@ -444,13 +457,6 @@ poor design of the data or methods on data objects #}
{{ post_form.text }}{# this element is resizable and will be wrapped by js #} - {% if wiki_on %} -
- {{ post_form.wiki }} - {{ post_form.wiki.label_tag() }} -
- {% endif %}
{# need label element for resizable input, b/c form validation won't find span #} diff --git a/askbot/skins/default/templates/question.html b/askbot/skins/default/templates/question.html index 927e2888..7eb21d21 100644 --- a/askbot/skins/default/templates/question.html +++ b/askbot/skins/default/templates/question.html @@ -349,7 +349,7 @@ {% endif %}

{% endif %} - {{macros.edit_post(answer, settings.WIKI_ON)}} + {{ macros.edit_post(answer) }} - {% endif %} + {% if settings.WIKI_ON %} + {{ macros.checkbox_in_div(answer.wiki) }} + {% endif %} + {% endif %}
{% endblock %} diff --git a/askbot/skins/default/templates/question_edit.html b/askbot/skins/default/templates/question_edit.html index 04c6c28c..6a3aae0c 100644 --- a/askbot/skins/default/templates/question_edit.html +++ b/askbot/skins/default/templates/question_edit.html @@ -15,16 +15,19 @@ id="select_revision" name="select_revision" value="{% trans %}select revision{% endtrans %}"> - {{macros.edit_post( - form, - settings.WIKI_ON and question.wiki == False, - post_type='question', - edit_title=True, - )}} + {{ macros.edit_post(form, post_type='question', edit_title=True,) }}
 
+
+ {% if settings.WIKI_ON and question.wiki == False %} + {{ macros.checkbox_in_div(form.wiki) }} + {% endif %} + {% if form.can_stay_anonymous() %} + {{ macros.checkbox_in_div(form.reveal_identity) }} + {% endif %} +
{% endblock %} diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py index c4c31fc4..fe09b283 100644 --- a/askbot/tests/db_api_tests.py +++ b/askbot/tests/db_api_tests.py @@ -53,6 +53,38 @@ class DBApiTests(AskbotTestCase): 1 ) + def ask_anonymous_question(self): + q = self.user.post_question( + is_anonymous = True, + body_text = 'hahahah', + title = 'aouaouaosuoa', + tags = 'test' + ) + return self.reload_object(q) + + def test_post_anonymous_question(self): + q = self.ask_anonymous_question() + self.assertTrue(q.is_anonymous) + rev = q.revisions.all()[0] + self.assertTrue(rev.is_anonymous) + + def test_reveal_asker_identity(self): + q = self.ask_anonymous_question() + self.other_user.set_status('m') + self.other_user.save() + self.other_user.edit_question( + question = q, + title = 'hahah', + body_text = 'hoeuaoea', + tags = 'aoeuaoeu', + revision_comment = 'hahahah' + ) + q.remove_author_anonymity() + q = self.reload_object(q) + self.assertFalse(q.is_anonymous) + for rev in q.revisions.all(): + self.assertFalse(rev.is_anonymous) + def test_accept_best_answer(self): self.post_answer(user = self.other_user) self.user.accept_best_answer(self.answer) diff --git a/askbot/tests/form_tests.py b/askbot/tests/form_tests.py index dfa2d628..1a18678c 100644 --- a/askbot/tests/form_tests.py +++ b/askbot/tests/form_tests.py @@ -31,5 +31,119 @@ class TagNamesFieldTests(AskbotTestCase): cleaned_tags = self.clean('tag1 taG2 TAG1 tag3 tag3') self.assert_tags_equal(cleaned_tags, ['TAG1', 'Tag2', 'tag3']) +class EditQuestionAnonymouslyFormTests(AskbotTestCase): + """setup the following truth table + on reveal_identity field: + is_anon can owner checked result + """ + truth_table = ( + (0, 0, 0, 0, False), + (0, 0, 0, 1, False), + (0, 0, 1, 0, False), + (0, 0, 1, 1, False), + (0, 1, 0, 0, False), + (0, 1, 0, 1, False), + (0, 1, 1, 0, False), + (0, 1, 1, 1, False),#all up to this point are False + (1, 0, 0, 0, False), + (1, 0, 0, 1, 'error'),#not owner + (1, 0, 1, 0, 'error'),#rules changed either reload page or check box + (1, 0, 1, 1, True),#rules changed - say yes here + (1, 1, 0, 0, False), + (1, 1, 0, 1, 'error'), + (1, 1, 1, 0, False), + (1, 1, 1, 1, True), + ) + #legend: is_anon - question is anonymous + # can - askbot_settings.ALLOW_ASK_ANONYMOUSLY + # owner - editor is question owner + # checked - the checkbox "reveal_identity" is marked + def setUp(self): + self.create_user() + self.create_user( + username = 'other_user', + status = 'm'#must be able to edit + ) + super(EditQuestionAnonymouslyFormTests, self).setUp() + + def setup_data(self, is_anon, can_be_anon, is_owner, box_checked): + """sets up data in the same order as shown in the + truth table above + + the four positional arguments are in the same order + """ + askbot_settings.update('ALLOW_ASK_ANONYMOUSLY', can_be_anon) + question = self.post_question(is_anonymous = is_anon) + if is_owner: + editor = self.user + else: + editor = self.other_user + data = { + 'tags': 'tag1 tag2', + 'text': 'ostaousohuosuh', + 'title': 'stahosetuhaoeudhuh' + } + if box_checked: + data['reveal_identity'] = 'on' + self.form = forms.EditQuestionForm( + data, + question = question, + user = editor, + revision = question.get_latest_revision(), + ) + + def test_reveal_identity_field(self): + """runs through the truth table and tests them items by one + """ + current_item = 0 + for entry in self.truth_table: + self.setup_data(*(entry[:4])) + + if self.form.is_valid(): + result = self.form.cleaned_data['reveal_identity'] + else: + result = 'error' + + error_message = 'failed truth table item %d' % current_item + current_item += 1 + expected_result = entry[4] + self.assertEquals(result, expected_result, error_message) + +class AskFormTests(AskbotTestCase): + + def setup_data(self, allow_anonymous = True, ask_anonymously = None): + askbot_settings.update('ALLOW_ASK_ANONYMOUSLY', allow_anonymous) + data = { + 'title': 'test title', + 'text': 'test content', + 'tags': 'test', + } + if ask_anonymously == True: + data['ask_anonymously'] = 'on' + self.form = forms.AskForm(data) + self.form.full_clean() + + def assert_anon_is(self, value): + self.assertEquals( + self.form.cleaned_data['ask_anonymously'], + value + ) + + def test_ask_anonymously_disabled(self): + """test that disabled anon postings yields False""" + self.setup_data(ask_anonymously = True, allow_anonymous = False) + self.assert_anon_is(False) + + def test_ask_anonymously_field_positive(self): + """check that the 'yes' selection goes through + """ + self.setup_data(ask_anonymously = True) + self.assert_anon_is(True) + + def test_ask_anonymously_field_negative(self): + """check that the 'no' selection goes through + """ + self.setup_data(ask_anonymously = False) + self.assert_anon_is(False) diff --git a/askbot/tests/utils.py b/askbot/tests/utils.py index 6942ce29..76eda149 100644 --- a/askbot/tests/utils.py +++ b/askbot/tests/utils.py @@ -77,6 +77,7 @@ class AskbotTestCase(TestCase): body_text = 'test question body text', tags = 'test', wiki = False, + is_anonymous = False, follow = False, timestamp = None ): @@ -94,6 +95,7 @@ class AskbotTestCase(TestCase): body_text = body_text, tags = tags, wiki = wiki, + is_anonymous = is_anonymous, timestamp = timestamp ) @@ -102,6 +104,11 @@ class AskbotTestCase(TestCase): return question + def reload_object(self, obj): + """reloads model object from the database + """ + return obj.__class__.objects.get(id = obj.id) + def post_answer( self, user = None, diff --git a/askbot/views/writers.py b/askbot/views/writers.py index 2bc4d3a3..aa062d87 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -197,7 +197,6 @@ def ask(request):#view used to ask a new question if request.method == "POST": form = forms.AskForm(request.POST) if form.is_valid(): - timestamp = datetime.datetime.now() #todo: move this to clean_title title = form.cleaned_data['title'].strip() @@ -205,17 +204,18 @@ def ask(request):#view used to ask a new question #todo: move this to clean_tagnames tagnames = form.cleaned_data['tags'].strip() text = form.cleaned_data['text'] + ask_anonymously = form.cleaned_data['ask_anonymously'] if request.user.is_authenticated(): - try: question = request.user.post_question( - title = title, - body_text = text, - tags = tagnames, - wiki = wiki, - timestamp = timestamp - ) + title = title, + body_text = text, + tags = tagnames, + wiki = wiki, + is_anonymous = ask_anonymously, + timestamp = timestamp + ) return HttpResponseRedirect(question.get_absolute_url()) except exceptions.PermissionDenied, e: request.user.message_set.create(message = unicode(e)) @@ -230,6 +230,7 @@ def ask(request):#view used to ask a new question title = title, tagnames = tagnames, wiki = wiki, + is_anonymous = ask_anonymously, text = text, summary = summary, added_at = timestamp, @@ -324,33 +325,66 @@ def edit_question(request, id): try: request.user.assert_can_edit_question(question) if request.method == 'POST': - if 'select_revision' in request.POST:#revert-type edit - # user has changed revistion number - revision_form = forms.RevisionForm(question, latest_revision, request.POST) + if 'select_revision' in request.POST: + #revert-type edit - user selected previous revision + revision_form = forms.RevisionForm( + question, + latest_revision, + request.POST + ) if revision_form.is_valid(): # Replace with those from the selected revision - form = forms.EditQuestionForm(question, - models.QuestionRevision.objects.get(question=question, - revision=revision_form.cleaned_data['revision'])) + rev_id = revision_form.cleaned_data['revision'] + selected_revision = models.QuestionRevision.objects.get( + question = question, + revision = rev_id + ) + form = forms.EditQuestionForm( + question = question, + user = request.user, + revision = selected_revision + ) else: - form = forms.EditQuestionForm(question, latest_revision, request.POST) + form = forms.EditQuestionForm( + request.POST, + question = question, + user = request.user, + revision = latest_revision + ) else:#new content edit # Always check modifications against the latest revision - form = forms.EditQuestionForm(question, latest_revision, request.POST) + form = forms.EditQuestionForm( + request.POST, + question = question, + revision = latest_revision, + user = request.user, + ) if form.is_valid(): if form.has_changed(): + + if form.cleaned_data['reveal_identity']: + question.remove_author_anonymity() + + is_anon_edit = form.cleaned_data['edit_anonymously'] + is_wiki = form.cleaned_data.get('wiki', question.wiki) request.user.edit_question( - question = question, - title = form.cleaned_data['title'], - body_text = form.cleaned_data['text'], - revision_comment = form.cleaned_data['summary'], - tags = form.cleaned_data['tags'], - wiki = form.cleaned_data.get('wiki', question.wiki) - ) + question = question, + title = form.cleaned_data['title'], + body_text = form.cleaned_data['text'], + revision_comment = form.cleaned_data['summary'], + tags = form.cleaned_data['tags'], + wiki = is_wiki, + edit_anonymously = is_anon_edit, + ) return HttpResponseRedirect(question.get_absolute_url()) else: + #request type was "GET" revision_form = forms.RevisionForm(question, latest_revision) - form = forms.EditQuestionForm(question, latest_revision) + form = forms.EditQuestionForm( + question = question, + revision = latest_revision, + user = request.user + ) data = { 'active_tab': 'questions', -- cgit v1.2.3-1-g7c22