diff options
-rw-r--r-- | askbot/migrations/0135_auto__add_askwidget.py | 364 | ||||
-rw-r--r-- | askbot/models/__init__.py | 29 | ||||
-rw-r--r-- | askbot/models/widgets.py | 39 | ||||
-rwxr-xr-x | askbot/skins/default/media/style/askbot-modal.css | 10 | ||||
-rw-r--r-- | askbot/skins/default/templates/create_ask_widget.html | 0 | ||||
-rw-r--r-- | askbot/skins/default/templates/list_ask_widget.html | 0 | ||||
-rwxr-xr-x | askbot/skins/default/templates/widgets/askbot_widget.js | 19 | ||||
-rw-r--r-- | askbot/tests/widget_tests.py | 35 | ||||
-rw-r--r-- | askbot/urls.py | 12 | ||||
-rw-r--r-- | askbot/views/widgets.py | 46 |
10 files changed, 530 insertions, 24 deletions
diff --git a/askbot/migrations/0135_auto__add_askwidget.py b/askbot/migrations/0135_auto__add_askwidget.py new file mode 100644 index 00000000..ce95d5e6 --- /dev/null +++ b/askbot/migrations/0135_auto__add_askwidget.py @@ -0,0 +1,364 @@ +# 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 model 'AskWidget' + db.create_table('askbot_askwidget', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('group', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='groups', null=True, to=orm['askbot.Tag'])), + ('default_group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Tag'], null=True, blank=True)), + ('inner_style', self.gf('django.db.models.fields.TextField')(default='')), + ('outer_style', self.gf('django.db.models.fields.TextField')(default='')), + )) + db.send_create_signal('askbot', ['AskWidget']) + + + def backwards(self, orm): + + # Deleting model 'AskWidget' + db.delete_table('askbot_askwidget') + + + 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.askwidget': { + 'Meta': {'object_name': 'AskWidget'}, + 'default_group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'groups'", 'null': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'inner_style': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'outer_style': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + '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', 'db_index': 'True'}) + }, + 'askbot.draftanswer': { + 'Meta': {'object_name': 'DraftAnswer'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"}) + }, + 'askbot.draftquestion': { + 'Meta': {'object_name': 'DraftQuestion'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}) + }, + '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': {'unique_together': "(('group', 'user'),)", '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'}), + 'is_open': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}), + 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': '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']"}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Tag']"}), + '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.postflagreason': { + 'Meta': {'object_name': 'PostFlagReason'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + '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'}), + 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + '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', [], {}), + '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.posttogroup': { + 'Meta': {'unique_together': "(('post', 'tag'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']"}) + }, + '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'", 'null': 'True', 'to': "orm['askbot.Post']"}), + 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}), + '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'}), + 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}), + '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']"}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}), + '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_signature': ('django.db.models.fields.TextField', [], {'blank': '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_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + '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'}), + 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + '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'] diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 5392fc30..a0ce96f7 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -45,6 +45,7 @@ from askbot.models.reply_by_email import ReplyAddress from askbot.models import signals from askbot.models.badges import award_badges_signal, get_badge, BadgeData from askbot.models.repute import Award, Repute, Vote +from askbot.models.widgets import AskWidget from askbot import auth from askbot.utils.decorators import auto_now_timestamp from askbot.utils.slug import slugify @@ -306,7 +307,7 @@ def user_get_marked_tag_names(self, reason): attr_name = MARKED_TAG_PROPERTY_MAP[reason] wildcard_tags = getattr(self, attr_name).split() tag_names.extend(wildcard_tags) - + return tag_names def user_has_affinity_to_question(self, question = None, affinity_type = None): @@ -377,7 +378,7 @@ def user_can_have_strong_url(self): return (self.reputation >= askbot_settings.MIN_REP_TO_HAVE_STRONG_URL) def user_can_post_by_email(self): - """True, if reply by email is enabled + """True, if reply by email is enabled and user has sufficient reputatiton""" return askbot_settings.REPLY_BY_EMAIL and \ self.reputation > askbot_settings.MIN_REP_TO_POST_BY_EMAIL @@ -2240,7 +2241,7 @@ def user_get_groups_membership_info(self, groups): info[group.id]['can_join'] = group.group_profile.can_accept_user(self) return info - + def user_get_karma_summary(self): @@ -2385,7 +2386,7 @@ def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None): auth.onDownVotedCanceled(vote, post, user, timestamp) else: auth.onDownVoted(vote, post, user, timestamp) - + post.thread.invalidate_cached_data() if post.post_type == 'question': @@ -2477,12 +2478,12 @@ def flag_post(user, post, timestamp=None, cancel=False, cancel_all = False, forc content_type = post_content_type, object_id=post.id ) for flag in all_flags: - auth.onUnFlaggedItem(post, flag.user, timestamp=timestamp) + auth.onUnFlaggedItem(post, flag.user, timestamp=timestamp) elif cancel:#todo: can't unflag? if force == False: user.assert_can_remove_flag_offensive(post = post) - auth.onUnFlaggedItem(post, user, timestamp=timestamp) + auth.onUnFlaggedItem(post, user, timestamp=timestamp) else: if force == False: @@ -2836,17 +2837,17 @@ def format_instant_notification_email( if update_type.endswith('update'): user_action = _('%(user)s edited a %(post_link)s.') else: - user_action = _('%(user)s posted a %(post_link)s') + user_action = _('%(user)s posted a %(post_link)s') elif post.is_answer(): if update_type.endswith('update'): user_action = _('%(user)s edited an %(post_link)s.') else: - user_action = _('%(user)s posted an %(post_link)s.') + user_action = _('%(user)s posted an %(post_link)s.') elif post.is_question(): if update_type.endswith('update'): user_action = _('%(user)s edited a %(post_link)s.') else: - user_action = _('%(user)s posted a %(post_link)s.') + user_action = _('%(user)s posted a %(post_link)s.') else: raise ValueError('unrecognized post type') @@ -2874,7 +2875,7 @@ def format_instant_notification_email( reply_separator += '</p>' else: reply_separator = user_action - + update_data = { 'update_author_name': from_user.username, 'receiving_user_name': to_user.username, @@ -2903,7 +2904,7 @@ def get_reply_to_addresses(user, post): the first address - always a real email address, the second address is not ``None`` only for "question" posts. - When the user is notified of a new question - + When the user is notified of a new question - i.e. `post` is a "quesiton", he/she will need to choose - whether to give a question or a comment, thus we return the second address - for the comment reply. @@ -2989,7 +2990,7 @@ def send_instant_notifications_about_activity_in_post( update_type = update_type, template = get_template('instant_notification.html') ) - + headers['Reply-To'] = reply_address mail.send_mail( subject_line = subject_line, @@ -3010,7 +3011,7 @@ def notify_author_of_published_revision( if revision.should_notify_author_about_publishing(was_approved): from askbot.tasks import notify_author_of_published_revision_celery_task notify_author_of_published_revision_celery_task.delay(revision) - + #todo: move to utils def calculate_gravatar_hash(instance, **kwargs): @@ -3425,7 +3426,7 @@ def update_user_avatar_type_flag(instance, **kwargs): def make_admin_if_first_user(instance, **kwargs): """first user automatically becomes an administrator the function is run only once in the interpreter session - """ + """ import sys #have to check sys.argv to satisfy the test runner #which fails with the cache-based skipping diff --git a/askbot/models/widgets.py b/askbot/models/widgets.py new file mode 100644 index 00000000..ca21f7a4 --- /dev/null +++ b/askbot/models/widgets.py @@ -0,0 +1,39 @@ +from django.db import models +from django.contrib.auth.models import User +from django.utils.translation import ugettext as _ +from askbot.conf import settings as askbot_settings +from askbot.models import Tag +from django import forms + +DEFAULT_INNER_STYLE = '' + +DEFAULT_OUTER_STYLE = '' + +class AskWidget(models.Model): + '''stores widgets styles and options''' + title = models.CharField(max_length=100) + group = models.ForeignKey(Tag, null=True, blank=True, + related_name='groups') + default_group = models.ForeignKey(Tag, null=True, blank=True) + + inner_style = models.TextField(default=DEFAULT_INNER_STYLE, blank=True) + outer_style= models.TextField(default=DEFAULT_OUTER_STYLE, blank=True) + + class Meta: + app_label = 'askbot' + + + def embed_code(self): + pass + + def __unicode__(self): + return "Widget: %s" % self.title + +class CreateAskWidgetForm(forms.ModelForm): + inner_style = forms.CharField(widget=forms.Textarea, required=False, + initial=DEFAULT_INNER_STYLE) + outer_style = forms.CharField(widget=forms.Textarea, required=False, + initial=DEFAULT_OUTER_STYLE) + + class Meta: + model = AskWidget diff --git a/askbot/skins/default/media/style/askbot-modal.css b/askbot/skins/default/media/style/askbot-modal.css new file mode 100755 index 00000000..59121f0e --- /dev/null +++ b/askbot/skins/default/media/style/askbot-modal.css @@ -0,0 +1,10 @@ +#AskbotAskWidget { + visibility: hidden; + position: absolute; + left: 0px; + top: 0px; + width:100%; + height:100%; + text-align:center; + z-index: 1000; +} diff --git a/askbot/skins/default/templates/create_ask_widget.html b/askbot/skins/default/templates/create_ask_widget.html new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/askbot/skins/default/templates/create_ask_widget.html diff --git a/askbot/skins/default/templates/list_ask_widget.html b/askbot/skins/default/templates/list_ask_widget.html new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/askbot/skins/default/templates/list_ask_widget.html diff --git a/askbot/skins/default/templates/widgets/askbot_widget.js b/askbot/skins/default/templates/widgets/askbot_widget.js new file mode 100755 index 00000000..14949121 --- /dev/null +++ b/askbot/skins/default/templates/widgets/askbot_widget.js @@ -0,0 +1,19 @@ +var AskbotAskWidget = { + element_id: "AskbotAskWidget", + widgetToggle: function() { + element = document.getElementById(this.element_id); + element.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible"; + }, + toHtml: function() { + document.write('<link rel="stylesheet" href="https://{{request.get_host}}/embed.css"/>'); + {%if widget.outer_style %} + document.write('<style>{{widget.outer_style}}</style>'); + {%endif%} + //creating the div + document.write("<div id='" + this.element_id + "'>"); + document.write("<iframe src='{% url widget_ask %}' />"); + document.write("</div>"); + } +}; + +AskbotAskWidget.toHtml(); diff --git a/askbot/tests/widget_tests.py b/askbot/tests/widget_tests.py index 40c63e0e..11a7036f 100644 --- a/askbot/tests/widget_tests.py +++ b/askbot/tests/widget_tests.py @@ -11,6 +11,7 @@ class WidgetViewsTests(AskbotTestCase): def setUp(self): self.client = Client() + self.widget = models.AskWidget.objects.create(title='foo widget') self.user = self.create_user('user1') self.user.set_password('sample') self.user.save() @@ -19,12 +20,13 @@ class WidgetViewsTests(AskbotTestCase): def test_post_with_auth(self): self.client.login(username='user1', password='sample') - response = self.client.post(reverse('ask_by_widget'), self.good_data) + response = self.client.post(reverse('ask_by_widget', args=(self.widget.id, )), self.good_data) self.assertEquals(response.status_code, 302) self.client.logout() def test_post_without_auth(self): - response = self.client.post(reverse('ask_by_widget'), self.good_data) + #weird issue + response = self.client.post(reverse('ask_by_widget', args=(self.widget.id, )), self.good_data) self.assertEquals(response.status_code, 302) self.assertTrue('widget_question' in self.client.session) self.assertEquals(self.client.session['widget_question']['title'], @@ -45,7 +47,7 @@ class WidgetViewsTests(AskbotTestCase): session = self.client.session session['widget_question'] = widget_question_data session.save() - response = self.client.get(reverse('ask_by_widget'), + response = self.client.get(reverse('ask_by_widget', args=(self.widget.id, )), {'action': 'post-after-login'}) self.assertFalse('widget_question' in self.client.session) self.assertEquals(response.status_code, 302) @@ -59,3 +61,30 @@ class WidgetLoginViewTest(AskbotTestCase): template_name = 'authopenid/widget_signin.html' templates = [template.name for template in response.templates] self.assertTrue(template_name in templates) + +class WidgetCreatorViewsTests(AskbotTestCase): + + def setUp(self): + self.client = Client() + self.user = self.create_user('user1') + self.user.set_password('testpass') + self.user.set_admin_status() + self.user.save() + + def test_list_ask_widget_view(self): + self.client.login(username='user1', password='testpass') + response = self.client.get(reverse('list_ask_widgets')) + self.assertEquals(response.status_code, 200) + self.assertTrue('widgets' in response.context) + + def test_create_ask_widget_get(self): + self.client.login(username='user1', password='testpass') + response = self.client.get(reverse('create_ask_widget')) + self.assertEquals(response.status_code, 200) + self.assertTrue('form' in response.context) + + def test_create_ask_widget_post(self): + self.client.login(username='user1', password='testpass') + post_data = {'title': 'Test widget'} + response = self.client.post(reverse('create_ask_widget'), post_data) + self.assertEquals(response.status_code, 302) diff --git a/askbot/urls.py b/askbot/urls.py index cee5752a..fee4ad64 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -382,7 +382,7 @@ urlpatterns = patterns('', ), #widgets url! url( - r'^widgets/ask/$', + r'^widgets/ask/(?P<widget_id>\d+)/$', views.widgets.ask_widget, name = 'ask_by_widget' ), @@ -392,6 +392,16 @@ urlpatterns = patterns('', name = 'ask_by_widget_complete' ), url( + r'^widgets/ask/create/$', + views.widgets.create_ask_widget, + name = 'create_ask_widget' + ), + url( + r'^widgets/ask/$', + views.widgets.list_ask_widget, + name = 'list_ask_widgets' + ), + url( r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}, diff --git a/askbot/views/widgets.py b/askbot/views/widgets.py index 77eb7eb5..f8b9577f 100644 --- a/askbot/views/widgets.py +++ b/askbot/views/widgets.py @@ -2,19 +2,21 @@ from datetime import datetime from django.core import exceptions from django.utils import simplejson -from django.shortcuts import redirect +from django.http import HttpResponse from django.views.decorators import csrf from django.contrib.auth.models import User from django.core.urlresolvers import reverse +from django.shortcuts import redirect, get_object_or_404 from django.contrib.auth.decorators import login_required -from askbot.skins.loaders import render_into_skin +from askbot.skins.loaders import render_into_skin, get_template +from askbot.utils import decorators from askbot import models from askbot import forms @csrf.csrf_protect -def ask_widget(request): +def ask_widget(request, widget_id): def post_question(data, request): thread = models.Thread.objects.create_new(**data_dict) @@ -22,6 +24,7 @@ def ask_widget(request): request.session['widget_question_url'] = question.get_absolute_url() return question + widget = get_object_or_404(models.AskWidget, id=widget_id) if request.method == "POST": form = forms.AskWidgetForm(request.POST) @@ -42,7 +45,8 @@ def ask_widget(request): return redirect('ask_by_widget_complete') else: request.session['widget_question'] = data_dict - next_url = '%s?next=%s' % (reverse('widget_signin'), reverse('ask_by_widget')) + next_url = '%s?next=%s' % (reverse('widget_signin'), + reverse('ask_by_widget', args=(widget.id,))) return redirect(next_url) else: if 'widget_question' in request.session and \ @@ -59,16 +63,46 @@ def ask_widget(request): return redirect(next_url) form = forms.AskWidgetForm() - data = {'form': form} + data = {'form': form, 'widget': widget} return render_into_skin('ask_by_widget.html', data, request) @login_required def ask_widget_complete(request): question_url = request.session.get('widget_question_url') + custom_css = request.session.get('widget_css') if question_url: del request.session['widget_question_url'] else: question_url = '#' - data = {'question_url': question_url} + if custom_css: + del request.session['widget_css'] + + data = {'question_url': question_url, 'custom_css': custom_css} return render_into_skin('ask_widget_complete.html', data, request) + + +@decorators.admins_only +def list_ask_widget(request): + widgets = models.AskWidget.objects.all() + data = {'widgets': widgets} + return render_into_skin('list_ask_widget.html', data, request) + +@decorators.admins_only +def create_ask_widget(request): + if request.method=='POST': + form = models.widgets.CreateAskWidgetForm(request.POST) + if form.is_valid(): + form.save() + return redirect('list_ask_widgets') + else: + form = models.widgets.CreateAskWidgetForm() + + data = {'form': form} + return render_into_skin('create_ask_widget.html', data, request) + +#TODO: Add cache +def render_ask_widget_js(request, widget_id): + widget = get_object_or_404(models.AskWidget) + content = get_template('widgets/askbot_widget.js') + return HttpResponse(content, mimetype='text/javascript') |