]> git.openstreetmap.org Git - osqa.git/commitdiff
Merging jambazov --> trunk.
authorjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Mon, 7 Mar 2011 22:10:47 +0000 (22:10 +0000)
committerjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Mon, 7 Mar 2011 22:10:47 +0000 (22:10 +0000)
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@796 0cfe37f9-358a-4d5e-be75-b63607b5c754

24 files changed:
forum/actions/node.py
forum/actions/user.py
forum/forms/qanda.py
forum/management/commands/maintaindb.py [new file with mode: 0644]
forum/middleware/admin_messages.py [new file with mode: 0644]
forum/migrations/0045_auto__add_openidassociation__add_openidnonce__add_field_tag_created_at.py [new file with mode: 0644]
forum/migrations/0046_set_tag_dates.py [new file with mode: 0644]
forum/models/node.py
forum/models/tag.py
forum/settings/email.py
forum/settings/forms.py
forum/settings/repgain.py
forum/settings/view.py
forum/skins/default/media/js/osqa.admin.js
forum/skins/default/media/style/djstyle_admin.css
forum/skins/default/templates/node/reviser_info.html
forum/skins/default/templates/osqaadmin/mail_test.html [new file with mode: 0644]
forum/skins/default/templates/osqaadmin/test_email_settings.html [new file with mode: 0644]
forum/urls.py
forum/views/admin.py
forum/views/auth.py
forum/views/readers.py
forum_modules/sximporter/importer.py
settings.py

index cd39924d0c22ccb9c922a84f83f23cf717506b29..530c649299809992a20249661e40a8e06b1df228 100644 (file)
@@ -2,6 +2,7 @@ from django.utils.html import strip_tags
 from django.utils.translation import ugettext as _\r
 from forum.models.action import ActionProxy\r
 from forum.models import Comment, Question, Answer, NodeRevision\r
+import logging\r
 \r
 class NodeEditAction(ActionProxy):\r
     def create_revision_data(self, initial=False, **data):\r
@@ -201,6 +202,8 @@ class AnswerToQuestionAction(ActionProxy):
     def process_data(self, title):\r
         self.node.node_type = "question"\r
         self.node.title = title\r
+        self.node.active_revision.title = title\r
+        self.node.active_revision.save()\r
         self.node.last_edited = self\r
         self.node.update_last_activity(self.user, save=True)\r
 \r
index 50d1a7357a035edb94ea39a79c5fe3e24624fae2..5b818c72a4ca4e4ca71dab5d7a361f8ca713d99b 100644 (file)
@@ -23,6 +23,23 @@ class UserJoinsAction(ActionProxy):
         'app_name': APP_SHORT_NAME,
         }
 
+class EmailValidationAction(ActionProxy):
+    verb = _("validated e-mail")
+
+    def repute_users(self):
+        self.repute(self.user, int(settings.REP_GAIN_BY_EMAIL_VALIDATION))
+
+    def process_action(self):
+        self.user.email_isvalid = True
+        self.user.save()
+
+    def describe(self, viewer=None):
+        return _("%(user)s %(have_has)s validated the e-mail %(email)s") % {
+        'user': self.hyperlink(self.user.get_profile_url(), self.friendly_username(viewer, self.user)),
+        'have_has': self.viewer_or_user_verb(viewer, self.user, _('have'), _('has')),
+        'email' : self.user.email
+        }
+
 class EditProfileAction(ActionProxy):
     verb = _("edited profile")
 
index 98906400a5d2da211136303f8944899060513203..e107ad2f53691a118c24dc7c6447cfcd0080eb66 100644 (file)
@@ -17,8 +17,8 @@ class TitleField(forms.CharField):
     def __init__(self, *args, **kwargs):
         super(TitleField, self).__init__(*args, **kwargs)
         self.required = True
-        self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off'})
         self.max_length = 255
+        self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off', 'maxlength' : self.max_length})
         self.label  = _('title')
         self.help_text = _('please enter a descriptive title for your question')
         self.initial = ''
diff --git a/forum/management/commands/maintaindb.py b/forum/management/commands/maintaindb.py
new file mode 100644 (file)
index 0000000..a2fc8de
--- /dev/null
@@ -0,0 +1,66 @@
+from django.core.management.base import BaseCommand, CommandError
+from forum.models import Node, NodeRevision
+
+import logging
+
+# Used to activate the latest revision connected to some node
+def activate_latest_revision(node):
+    # We're adding a new try-except block just in case that function has been called incorrectly.
+    try:
+        # The latest revision is the one that was added the last.
+        rev = node.revisions.all().order_by('-pk')[0]
+        node.active_revision_id = rev.id
+        node.save()
+
+        return rev
+    except:
+        logging.error("Incorrect attempt to activate the latest revision of a node \
+                       that has no revisions at all has been made.")
+        return None
+
+# Used to create a new NodeRevision object according to the node content
+def create_revision(node):
+    rev = NodeRevision(
+            author_id = node.author_id,
+            body = node.body,
+            node_id = node.id,
+            revised_at = node.added_at,
+            revision = 1,
+            summary = 'Initial revision',
+            tagnames = node.tagnames,
+            title = node.title,
+            )
+
+    node.save()
+
+    return node
+
+class Command(BaseCommand):
+
+    def handle(self,*args, **options):
+        print 'Running MaintainDb'
+
+        nodes = Node.objects.all()
+
+        for node in nodes:
+            if node.active_revision is None:
+                print "Node #%(node_id)d: NodeRevision doesn't exist" % dict(node_id=node.id)
+
+                # We currently don't have any active revision for this Node. Let's check if there are any revisions
+                # at all for it. If there are any we activate the last.
+                if node.revisions.all().count() > 0:
+                    print "  We have revisions for Node #%(node_id)d." % dict(node_id=node.id)
+
+                    # If there are already some revisions connected to the current node, we activate the latest
+                    activate_latest_revision(node)
+                else:
+                    print "  We don't have revisions for Node #%(node_id)d. We're "\
+                          "going to create a new one from the current node content."% dict(node_id=node.id)
+
+                    # First of all we're going to create a new revision according to the current node data...
+                    create_revision(node)
+
+                    # ...and after that we're going to activate it
+                    activate_latest_revision(node)
+
+                    #print rev.node
diff --git a/forum/middleware/admin_messages.py b/forum/middleware/admin_messages.py
new file mode 100644 (file)
index 0000000..2d391de
--- /dev/null
@@ -0,0 +1,60 @@
+from forum.user_messages import create_message
+from django.utils.translation import ugettext as _
+from django.core.urlresolvers import reverse
+from django.core.exceptions import ObjectDoesNotExist
+
+from forum.settings import EMAIL_HOST, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, \
+        APP_URL
+
+class AdminMessagesMiddleware(object):
+    def process_request(self, request):
+        # Check if the email settings are configured
+        self.check_email_settings(request)
+
+        # Check if the APP_URL setting is configured
+        self.check_app_url(request)
+
+    def check_email_settings(self, request):
+        # We want to warn only the administrators that the email settings are not configured.
+        # So, first of all we check if we're dealing with the administrators and after that if
+        # the SMTP settings are configured at all. We suppose that the SMTP settings are not configured
+        # if the EMAIL_HOST, the EMAIL_HOST_USER and the EMAIL_HOST_PASSWORD are not set at all.
+        if request.user.is_authenticated and request.user.is_staff and request.user.is_superuser and \
+            EMAIL_HOST == '' and EMAIL_HOST_USER == '' and EMAIL_HOST_PASSWORD == '':
+
+            msg = _("""
+                    The e-mail settings of this community are not configured yet. We strongly recommend you to
+                    do that from the <a href="%(email_settings_url)s">e-mail settings page</a> as soon as possible.
+                    """ % dict(email_settings_url=reverse('admin_set', kwargs={'set_name':'email'})))
+
+            # We do not want to repeat ourselves. If the message already exists in the message list, we're not going to
+            # add it. That's why first of all we're going the check if it is there.
+            try:
+                # If the message doesn't exist in the RelatedManager ObjectsDoesNotExist is going to be raised.
+                request.user.message_set.all().get(message=msg)
+            except ObjectDoesNotExist:
+                # Let's create the message.
+                request.user.message_set.create(message=msg)
+            except:
+                pass
+
+    def check_app_url(self, request):
+        # We consider the APP_URL setting not configured if it contains only the protocol
+        # name or if it's shorter than 7 characters.
+        if request.user.is_authenticated and request.user.is_staff and request.user.is_superuser and \
+            APP_URL == 'http://' or APP_URL == 'https://' or len(APP_URL) < 7:
+
+            msg = _("""
+                       Please, configure your APP_URL setting from the local settings file.
+                    """)
+
+            # We do not want to repeat ourselves. If the message already exists in the message list, we're not going to
+            # add it. That's why first of all we're going the check if it is there.
+            try:
+                # If the message doesn't exist in the RelatedManager ObjectsDoesNotExist is going to be raised.
+                request.user.message_set.all().get(message=msg)
+            except ObjectDoesNotExist:
+                # Let's create the message.
+                request.user.message_set.create(message=msg)
+            except:
+                pass
diff --git a/forum/migrations/0045_auto__add_openidassociation__add_openidnonce__add_field_tag_created_at.py b/forum/migrations/0045_auto__add_openidassociation__add_openidnonce__add_field_tag_created_at.py
new file mode 100644 (file)
index 0000000..898d5c0
--- /dev/null
@@ -0,0 +1,270 @@
+# 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 'Tag.created_at'
+        db.add_column('forum_tag', 'created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, null=True, blank=True), keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Tag.created_at'
+        db.delete_column('forum_tag', 'created_at')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', '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'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            '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'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'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'})
+        },
+        'forum.action': {
+            'Meta': {'object_name': 'Action'},
+            'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}),
+            'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}),
+            'real_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'proxied_actions'", 'null': 'True', 'to': "orm['forum.User']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"})
+        },
+        'forum.actionrepute': {
+            'Meta': {'object_name': 'ActionRepute'},
+            'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}),
+            'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}),
+            'value': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+        },
+        'forum.authkeyuserassociation': {
+            'Meta': {'object_name': 'AuthKeyUserAssociation'},
+            'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"})
+        },
+        'forum.award': {
+            'Meta': {'unique_together': "(('user', 'badge', 'node'),)", 'object_name': 'Award'},
+            'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'award'", 'unique': 'True', 'to': "orm['forum.Action']"}),
+            'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.Badge']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}),
+            'trigger': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'null': 'True', 'to': "orm['forum.Action']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
+        },
+        'forum.badge': {
+            'Meta': {'object_name': 'Badge'},
+            'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}),
+            'cls': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'type': ('django.db.models.fields.SmallIntegerField', [], {})
+        },
+        'forum.flag': {
+            'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Flag'},
+            'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'flag'", 'unique': 'True', 'to': "orm['forum.Action']"}),
+            'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flags'", 'to': "orm['forum.Node']"}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flags'", 'to': "orm['forum.User']"})
+        },
+        'forum.keyvalue': {
+            'Meta': {'object_name': 'KeyValue'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'})
+        },
+        'forum.markedtag': {
+            'Meta': {'object_name': 'MarkedTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"})
+        },
+        'forum.node': {
+            'Meta': {'object_name': 'Node'},
+            'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}),
+            'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}),
+            'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}),
+            'body': ('django.db.models.fields.TextField', [], {}),
+            'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}),
+            'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}),
+            'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}),
+            'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'state_string': ('django.db.models.fields.TextField', [], {'default': "''"}),
+            'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+        },
+        'forum.noderevision': {
+            'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}),
+            'body': ('django.db.models.fields.TextField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}),
+            'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+            'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+        },
+        'forum.nodestate': {
+            'Meta': {'unique_together': "(('node', 'state_type'),)", 'object_name': 'NodeState'},
+            'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'node_state'", 'unique': 'True', 'to': "orm['forum.Action']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['forum.Node']"}),
+            'state_type': ('django.db.models.fields.CharField', [], {'max_length': '16'})
+        },
+        'forum.openidassociation': {
+            'Meta': {'object_name': 'OpenIdAssociation'},
+            'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}),
+            'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issued': ('django.db.models.fields.IntegerField', [], {}),
+            'lifetime': ('django.db.models.fields.IntegerField', [], {}),
+            'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}),
+            'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'})
+        },
+        'forum.openidnonce': {
+            'Meta': {'object_name': 'OpenIdNonce'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'timestamp': ('django.db.models.fields.IntegerField', [], {})
+        },
+        'forum.questionsubscription': {
+            'Meta': {'object_name': 'QuestionSubscription'},
+            'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 3, 18, 19, 21, 244056)'}),
+            'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
+        },
+        'forum.subscriptionsettings': {
+            'Meta': {'object_name': 'SubscriptionSettings'},
+            'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+            'new_question': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+            'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}),
+            'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'send_digest': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"})
+        },
+        'forum.tag': {
+            'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}),
+            'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'forum.user': {
+            'Meta': {'object_name': 'User', '_ormbases': ['auth.User']},
+            'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
+            'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+        },
+        'forum.userproperty': {
+            'Meta': {'unique_together': "(('user', 'key'),)", 'object_name': 'UserProperty'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'properties'", 'to': "orm['forum.User']"}),
+            'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'})
+        },
+        'forum.validationhash': {
+            'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'},
+            'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 4, 18, 19, 21, 460663)'}),
+            'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
+        },
+        'forum.vote': {
+            'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Vote'},
+            'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'vote'", 'unique': 'True', 'to': "orm['forum.Action']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.Node']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.User']"}),
+            'value': ('django.db.models.fields.SmallIntegerField', [], {}),
+            'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
+        }
+    }
+
+    complete_apps = ['forum']
diff --git a/forum/migrations/0046_set_tag_dates.py b/forum/migrations/0046_set_tag_dates.py
new file mode 100644 (file)
index 0000000..78081a7
--- /dev/null
@@ -0,0 +1,279 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+from forum.models import Tag, Question
+from urllib import unquote
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        for tag in Tag.objects.all():
+            try:
+                question = Question.objects.filter(tags__name=unquote(tag.name)).order_by('pk')[0]
+                date = question.added_at
+                tag.created_at = date
+                tag.save()
+                print str(date)
+            except:
+                pass
+
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', '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'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            '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'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'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'})
+        },
+        'forum.action': {
+            'Meta': {'object_name': 'Action'},
+            'action_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'action_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'canceled_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+            'canceled_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'canceled_actions'", 'null': 'True', 'to': "orm['forum.User']"}),
+            'canceled_ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'null': 'True', 'to': "orm['forum.Node']"}),
+            'real_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'proxied_actions'", 'null': 'True', 'to': "orm['forum.User']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['forum.User']"})
+        },
+        'forum.actionrepute': {
+            'Meta': {'object_name': 'ActionRepute'},
+            'action': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.Action']"}),
+            'by_canceled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reputes'", 'to': "orm['forum.User']"}),
+            'value': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+        },
+        'forum.authkeyuserassociation': {
+            'Meta': {'object_name': 'AuthKeyUserAssociation'},
+            'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'provider': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_keys'", 'to': "orm['forum.User']"})
+        },
+        'forum.award': {
+            'Meta': {'unique_together': "(('user', 'badge', 'node'),)", 'object_name': 'Award'},
+            'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'award'", 'unique': 'True', 'to': "orm['forum.Action']"}),
+            'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'to': "orm['forum.Badge']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}),
+            'trigger': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'awards'", 'null': 'True', 'to': "orm['forum.Action']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
+        },
+        'forum.badge': {
+            'Meta': {'object_name': 'Badge'},
+            'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'through': "'Award'", 'to': "orm['forum.User']"}),
+            'cls': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'type': ('django.db.models.fields.SmallIntegerField', [], {})
+        },
+        'forum.flag': {
+            'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Flag'},
+            'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'flag'", 'unique': 'True', 'to': "orm['forum.Action']"}),
+            'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flags'", 'to': "orm['forum.Node']"}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flags'", 'to': "orm['forum.User']"})
+        },
+        'forum.keyvalue': {
+            'Meta': {'object_name': 'KeyValue'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'})
+        },
+        'forum.markedtag': {
+            'Meta': {'object_name': 'MarkedTag'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['forum.Tag']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['forum.User']"})
+        },
+        'forum.node': {
+            'Meta': {'object_name': 'Node'},
+            'abs_parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'all_children'", 'null': 'True', 'to': "orm['forum.Node']"}),
+            'active_revision': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'active'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.NodeRevision']"}),
+            'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nodes'", 'to': "orm['forum.User']"}),
+            'body': ('django.db.models.fields.TextField', [], {}),
+            'extra': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}),
+            'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'extra_ref': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']", 'null': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']", 'null': 'True'}),
+            'last_edited': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edited_node'", 'unique': 'True', 'null': 'True', 'to': "orm['forum.Action']"}),
+            'marked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'node_type': ('django.db.models.fields.CharField', [], {'default': "'node'", 'max_length': '16'}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'children'", 'null': 'True', 'to': "orm['forum.Node']"}),
+            'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'state_string': ('django.db.models.fields.TextField', [], {'default': "''"}),
+            'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'nodes'", 'to': "orm['forum.Tag']"}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+        },
+        'forum.noderevision': {
+            'Meta': {'unique_together': "(('node', 'revision'),)", 'object_name': 'NodeRevision'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'noderevisions'", 'to': "orm['forum.User']"}),
+            'body': ('django.db.models.fields.TextField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['forum.Node']"}),
+            'revised_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+            'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+        },
+        'forum.nodestate': {
+            'Meta': {'unique_together': "(('node', 'state_type'),)", 'object_name': 'NodeState'},
+            'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'node_state'", 'unique': 'True', 'to': "orm['forum.Action']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['forum.Node']"}),
+            'state_type': ('django.db.models.fields.CharField', [], {'max_length': '16'})
+        },
+        'forum.openidassociation': {
+            'Meta': {'object_name': 'OpenIdAssociation'},
+            'assoc_type': ('django.db.models.fields.TextField', [], {'max_length': '64'}),
+            'handle': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'issued': ('django.db.models.fields.IntegerField', [], {}),
+            'lifetime': ('django.db.models.fields.IntegerField', [], {}),
+            'secret': ('django.db.models.fields.TextField', [], {'max_length': '255'}),
+            'server_url': ('django.db.models.fields.TextField', [], {'max_length': '2047'})
+        },
+        'forum.openidnonce': {
+            'Meta': {'object_name': 'OpenIdNonce'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'salt': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'server_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'timestamp': ('django.db.models.fields.IntegerField', [], {})
+        },
+        'forum.questionsubscription': {
+            'Meta': {'object_name': 'QuestionSubscription'},
+            'auto_subscription': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_view': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 3, 19, 8, 39, 301998)'}),
+            'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.Node']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
+        },
+        'forum.subscriptionsettings': {
+            'Meta': {'object_name': 'SubscriptionSettings'},
+            'all_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'all_questions_watched_tags': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'enable_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'member_joins': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+            'new_question': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+            'new_question_watched_tags': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}),
+            'notify_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'notify_answers': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'notify_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'notify_comments_own_post': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'notify_reply_to_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'questions_answered': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'questions_asked': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'questions_commented': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'questions_viewed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'send_digest': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'subscribed_questions': ('django.db.models.fields.CharField', [], {'default': "'i'", 'max_length': '1'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'subscription_settings'", 'unique': 'True', 'to': "orm['forum.User']"})
+        },
+        'forum.tag': {
+            'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}),
+            'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['forum.User']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'marked_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'marked_tags'", 'through': "'MarkedTag'", 'to': "orm['forum.User']"}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+        },
+        'forum.user': {
+            'Meta': {'object_name': 'User', '_ormbases': ['auth.User']},
+            'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'bronze': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'gold': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'silver': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+            'subscriptions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribers'", 'through': "'QuestionSubscription'", 'to': "orm['forum.Node']"}),
+            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
+            'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+        },
+        'forum.userproperty': {
+            'Meta': {'unique_together': "(('user', 'key'),)", 'object_name': 'UserProperty'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'properties'", 'to': "orm['forum.User']"}),
+            'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'})
+        },
+        'forum.validationhash': {
+            'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'},
+            'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 3, 4, 19, 8, 39, 353678)'}),
+            'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"})
+        },
+        'forum.vote': {
+            'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Vote'},
+            'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'vote'", 'unique': 'True', 'to': "orm['forum.Action']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.Node']"}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.User']"}),
+            'value': ('django.db.models.fields.SmallIntegerField', [], {}),
+            'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
+        }
+    }
+
+    complete_apps = ['forum']
+
index ebe9139e6f1587c6df2c93b68537532bec9c8ee1..abd1f5a3b402f8cf946c1cdf626e77fb91c93dbc 100644 (file)
@@ -7,6 +7,7 @@ from django.utils.translation import ugettext as _
 from django.utils.safestring import mark_safe
 from django.utils.html import strip_tags
 from forum.utils.html import sanitize_html
+from forum.settings import SUMMARY_LENGTH
 from utils import PickledObjectField
 
 class NodeContent(models.Model):
@@ -304,7 +305,7 @@ class Node(BaseModel, NodeContent):
 
     @property
     def summary(self):
-        return strip_tags(self.html)[:300]
+        return strip_tags(self.html)[:SUMMARY_LENGTH]
 
     @models.permalink
     def get_revisions_url(self):
index 0bff00473d1268e698e191745938d49e4dabfac2..44e0a749aa40f0bdbd6fe274c59f374dc55c774f 100644 (file)
@@ -1,3 +1,4 @@
+import datetime
 from base import *
 
 from django.utils.translation import ugettext as _
@@ -11,6 +12,7 @@ class ActiveTagManager(models.Manager):
 class Tag(BaseModel):
     name            = models.CharField(max_length=255, unique=True)
     created_by      = models.ForeignKey(User, related_name='created_tags')
+    created_at      = models.DateTimeField(default=datetime.datetime.now, blank=True, null=True)
     marked_by       = models.ManyToManyField(User, related_name="marked_tags", through="MarkedTag")
     # Denormalised data
     used_count = models.PositiveIntegerField(default=0)
@@ -22,7 +24,7 @@ class Tag(BaseModel):
         app_label = 'forum'
 
     def __unicode__(self):
-        return self.name
+        return u'%s' % self.name
 
     def add_to_usage_count(self, value):
         if self.used_count + value < 0:
index 5e8bddfbafe27285035595fae3536c7a6ec8f032..ec271019efce6816aef2acb198e0036790d2e5f1 100644 (file)
@@ -2,6 +2,7 @@ from base import Setting, SettingSet
 from django.utils.translation import ugettext_lazy as _\r
 from django.forms.widgets import PasswordInput\r
 from django.forms.widgets import RadioSelect\r
+from forms import TestEmailSettingsWidget\r
 \r
 EMAIL_SET = SettingSet('email', _('Email settings'), _("Email server and other email related settings."), 50)\r
 \r
@@ -17,6 +18,12 @@ choices=EMAIL_SUBSCRIBE_CHOICES,
 help_text = _("Choose what should be the default email subscription status while registering."),\r
 required=False))\r
 \r
+TEST_EMAIL_SETTINGS = Setting('TEST_EMAIL_SETTINGS', '', EMAIL_SET, dict(\r
+label = _("E-Mail settings test"),\r
+help_text = _("Test the current E-Mail configuration."),\r
+required=False,\r
+widget=TestEmailSettingsWidget))\r
+\r
 EMAIL_HOST = Setting('EMAIL_HOST', '', EMAIL_SET, dict(\r
 label = _("Email Server"),\r
 help_text = _("The SMTP server through which your application will be sending emails."),\r
index 0c123011cbc99976a71a9107eba0f50a858fd850..3e3cc5afbf92833045346342b74d5b5c090df4aa 100644 (file)
@@ -4,6 +4,7 @@ from django import forms
 from forum.settings.base import Setting
 from django.utils.translation import ugettext as _
 from django.core.files.storage import FileSystemStorage
+from django.core.urlresolvers import reverse
 
 class DummySetting:
     pass
@@ -120,7 +121,20 @@ class CommaStringListWidget(forms.Textarea):
         else:
             return ', '.join(data[name])    
 
+class TestEmailSettingsWidget(forms.TextInput):
+    def render(self, name, value, attrs=None):
+        if not value:
+            value = ''
 
-
-
-
+        return """
+            <div id="test_email_settings">
+                <a href="%s" onclick="return false;" class="button test_button" href="/">Test</a>
+
+                <div style="margin-top: 7px">
+                <div style="display: none" class="ajax_indicator">
+                    Testing your current e-mail settings. Please, wait.
+                </div>
+                <div class="test_status"></div>
+                </div>
+            </div>
+            """ % reverse("test_email_settings")
\ No newline at end of file
index cfb566bbdf85019d9446cba04033b30dc7744de6..6b283ec2604ae95bf52fbe09929ff938cd7fc0b8 100644 (file)
@@ -11,6 +11,10 @@ MAX_REP_BY_UPVOTE_DAY = Setting('MAX_REP_BY_UPVOTE_DAY', 200, REP_GAIN_SET, dict
 label = "Max rep by up votes / day",\r
 help_text = _("Maximum reputation a user can gain in one day for being upvoted.")))\r
 \r
+REP_GAIN_BY_EMAIL_VALIDATION = Setting('REP_GAIN_BY_EMAIL_VALIDATION', 10, REP_GAIN_SET, dict(\r
+label = _("Rep gain by e-mail validation"),\r
+help_text = _("Reputation a user gains for validating his e-mail.")))\r
+\r
 REP_GAIN_BY_UPVOTED = Setting('REP_GAIN_BY_UPVOTED', 10, REP_GAIN_SET, dict(\r
 label = _("Rep gain by upvoted"),\r
 help_text = _("Reputation a user gains for having one of his posts up voted.")))\r
index 1eb44b08a8639cf643304e5588c0b4e5df46e338..fa6edf3d62dd3ba91722e81360bb79dd113df780 100644 (file)
@@ -4,6 +4,10 @@ from django.utils.translation import ugettext_lazy as _
 """ view settings """
 VIEW_SET = SettingSet('view', _('View settings'), _("Set up how certain parts of the site are displayed."), 20)
 
+SUMMARY_LENGTH = Setting('SUMMARY_LENGTH', 300, VIEW_SET, dict(
+label = _("Summary Length"),
+help_text = _("The number of characters that are going to be displayed in order to get the content summary.")))
+
 RECENT_TAGS_SIZE = Setting('RECENT_TAGS_SIZE', 25, VIEW_SET, dict(
 label = _("Recent tags block size"),
 help_text = _("The number of tags to display in the recent tags block in the front page.")))
index ee4feed1b8d1362525ccea4c97ff9b33493d3e1d..ccd71036d5ac2df8c3b8d9594e8e580e1e33ae64 100644 (file)
@@ -58,12 +58,22 @@ $(function() {
         $input.keyup(rewrite_anchor);\r
         rewrite_anchor();        \r
     });\r
+\r
+    $('#test_email_settings a.test_button').click(function() {\r
+        $('div.test_status').hide('slow')\r
+        $('div.ajax_indicator').show('fast')\r
+        $.post($(this).attr('href'), function(data) {\r
+            $('div.ajax_indicator').hide('fast')\r
+            $('div.test_status').html(data)\r
+            $('div.test_status').show('slow')\r
+        })\r
+    })\r
 });\r
 \r
 /*\r
  * Autocomplete - jQuery plugin 1.0.3\r
  *\r
- * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer\r
+ * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jrn Zaefferer\r
  *\r
  * Dual licensed under the MIT and GPL licenses:\r
  *   http://www.opensource.org/licenses/mit-license.php\r
index abc479b07c343a4f12caead896f977841a2e8df6..0ccd4ab237dd887d905e408f85df2cd2a44b099f 100644 (file)
@@ -54,4 +54,10 @@ input.longstring {
     background-color: #ffffe0;
     border: 3px double #b8860b;
     padding: 4px;
+}
+
+.ajax_indicator {
+    background: transparent url('../images/indicator.gif') top left no-repeat;
+    padding: 7px 25px;
+    min-height: 24px;
 }
\ No newline at end of file
index 8b4604720231498187807ed2814106940b7636ce..ac3df282bc898979b5e7bffeffaf06697d82728d 100644 (file)
@@ -3,7 +3,7 @@
     <p style="line-height:12px;">\r
         <strong>{% diff_date revision.revised_at %}</strong>\r
     </p>\r
-    {% gravatar revision.author 32 %}\r
-    <p><a href="{{ revised_at.author.get_profile_url }}">{{ revision.author.username }}</a><br/>\r
+    <a href="{{ revision.author.get_profile_url }}">{% gravatar revision.author 32 %}</a>\r
+    <p><a href="{{ revision.author.get_profile_url }}">{{ revision.author.username }}</a><br/>\r
     {% get_score_badge revision.author %}</p>\r
 </div>
\ No newline at end of file
diff --git a/forum/skins/default/templates/osqaadmin/mail_test.html b/forum/skins/default/templates/osqaadmin/mail_test.html
new file mode 100644 (file)
index 0000000..41bd944
--- /dev/null
@@ -0,0 +1,22 @@
+{% load i18n extra_tags email_tags %}
+
+{% declare %}
+    prefix = settings.EMAIL_SUBJECT_PREFIX
+    app_name = settings.APP_SHORT_NAME
+
+    exclude_finetune = True
+{% enddeclare %}
+
+{% email %}
+    {% subject %}{% blocktrans %}{{ prefix }} Your email settings are correct: {{ app_name }}{% endblocktrans %}{% endsubject %}
+
+    {% htmlcontent notifications/base.html %}
+        {% trans "If you see this content your E-Mail settings are correct." %}
+    {% endhtmlcontent %}
+
+{% textcontent notifications/base_text.html %}
+    {% trans "If you see this content your E-Mail settings are correct." %}
+{% endtextcontent %}
+
+{% endemail %}
+
diff --git a/forum/skins/default/templates/osqaadmin/test_email_settings.html b/forum/skins/default/templates/osqaadmin/test_email_settings.html
new file mode 100644 (file)
index 0000000..47a3ac5
--- /dev/null
@@ -0,0 +1,20 @@
+{% load i18n %}
+
+<p>
+    {%  blocktrans %}
+        An attempt has been made to send email to
+    {% endblocktrans %} {{ user.email }}.
+</p>
+
+<p>
+    {%  blocktrans %}
+        Please, check your inbox and if you see the new test message your email STMP settings are correct.
+    {% endblocktrans %}
+</p>
+
+<p>
+    {% blocktrans %}
+        If you do not see the new test message check your spam folder, and if you don't find it check your SMTP settings
+        again. It might be useful to take a look at the log file.
+    {%  endblocktrans %}
+</p>
\ No newline at end of file
index 1771e2c8ad7de1814cef13a43974ce6d824dd662..28175216aa20c91d0f31c7fbd15b74503d1ac3c4 100644 (file)
@@ -210,6 +210,9 @@ urlpatterns += patterns('',
                         url(r'^%s%s(?P<set_name>\w+)/$' % (_('admin/'), _('settings/')), app.admin.settings_set,
                             name="admin_set"),
 
+                        url(r'%s%s' % (_('admin/'), _('test_email_settings/')), app.admin.test_email_settings,
+                            name="test_email_settings"),
+
                         url(r'^feeds/rss[/]?$', app.readers.feed, name="latest_questions_feed"),
 
                         url(r'^(?P<path>.+)$', app.meta.page, name="static_page"),
index 40a098a3b470ff62938b7b8d1aaf49c342fab442..4b07915b1ff3a62777c39c333c958dd303f859eb 100644 (file)
@@ -13,6 +13,7 @@ from forum.settings.base import Setting
 from forum.forms import MaintenanceModeForm, PageForm, CreateUserForm
 from forum.settings.forms import SettingsSetForm
 from forum.utils import pagination, html
+from forum.utils.mail import send_template_email
 
 from forum.models import Question, Answer, User, Node, Action, Page, NodeState, Tag
 from forum.models.node import NodeMetaClass
@@ -548,7 +549,14 @@ def node_management(request):
     'hide_menu': True
     }))
 
+@super_user_required
+def test_email_settings(request):
+    user = request.user
 
+    send_template_email([user,], 'osqaadmin/mail_test.html', { 'user' : user })
 
-
-
+    return render_to_response(
+        'osqaadmin/test_email_settings.html',
+        { 'user': user, },
+        RequestContext(request)
+    )
\ No newline at end of file
index 3281fe44eebc22c8a38968d0ea352d54834a4440..f8eb897d06b5ea15a44cd6c488bc86b63f55a271 100644 (file)
@@ -27,7 +27,10 @@ from forum.authentication.base import InvalidAuthentication
 from forum.authentication import AUTH_PROVIDERS
 
 from forum.models import AuthKeyUserAssociation, ValidationHash, Question, Answer
-from forum.actions import UserJoinsAction
+from forum.actions import UserJoinsAction, EmailValidationAction
+from forum.models.action import ActionRepute
+
+from forum.settings import REP_GAIN_BY_EMAIL_VALIDATION
 
 def signin_page(request):
     referer = request.META.get('HTTP_REFERER', '/')
@@ -299,9 +302,14 @@ def validate_email(request, user, code):
     user = get_object_or_404(User, id=user)
 
     if (ValidationHash.objects.validate(code, user, 'email', [user.email])):
-        user.email_isvalid = True
-        user.save()
-        return login_and_forward(request, user, reverse('index'), _("Thank you, your email is now validated."))
+        EmailValidationAction(user=user, ip=request.META['REMOTE_ADDR']).save()
+        if REP_GAIN_BY_EMAIL_VALIDATION > 0:
+            message = _("Thank you, your email is now validated and you've got %d points." % int(REP_GAIN_BY_EMAIL_VALIDATION))
+
+        else:
+            message = _("Thank you, your email is now validated.")
+
+        return login_and_forward(request, user, reverse('index'), message)
     else:
         return render_to_response('auth/mail_already_validated.html', { 'user' : user }, RequestContext(request))
 
index 80709e72059b5a54a28ed854051fd8ccb34b4fa5..9a6f66eaf2caaa78ec8439ea4e8715cad8948201 100644 (file)
@@ -6,6 +6,7 @@ from forum import settings as django_settings
 from django.shortcuts import render_to_response, get_object_or_404
 from django.http import HttpResponseRedirect, HttpResponse, Http404, HttpResponsePermanentRedirect
 from django.core.paginator import Paginator, EmptyPage, InvalidPage
+from django.core.exceptions import ObjectDoesNotExist
 from django.template import RequestContext
 from django import template
 from django.utils.html import *
@@ -101,8 +102,13 @@ def questions(request):
 
 @decorators.render('questions.html')
 def tag(request, tag):
+    questions = Question.objects.filter(tags__name=unquote(tag))
+
+    if not questions:
+        raise Http404
+
     return question_list(request,
-                         Question.objects.filter(tags__name=unquote(tag)),
+                         questions,
                          mark_safe(_('questions tagged <span class="tag">%(tag)s</span>') % {'tag': tag}),
                          None,
                          mark_safe(_('Questions Tagged With %(tag)s') % {'tag': tag}),
index 44f90d78cdaf73a37539f6f186a161d3fdf9bbf7..0f7f812626e23968ff072493092218a5d984a383 100644 (file)
@@ -432,6 +432,7 @@ def postimport(dump, uidmap, tagmap):
         post.save()
 
         all.append(int(post.id))
+        create_and_activate_revision(post)
 
         del post
 
@@ -480,6 +481,8 @@ def comment_import(dump, uidmap, posts):
                 action_date = oc.added_at
                 )
 
+        create_and_activate_revision(oc)
+
         create_action.save()
         oc.save()
 
@@ -494,7 +497,6 @@ def add_tags_to_post(post, tagmap):
     tags = [tag for tag in [tagmap.get(name.strip()) for name in post.tagnames.split(u' ') if name] if tag]
     post.tagnames = " ".join([t.name for t in tags]).strip()
     post.tags = tags
-    create_and_activate_revision(post)
 
 
 def create_and_activate_revision(post):
@@ -773,6 +775,8 @@ def pages_import(dump, currid):
                 author_id = 1
                 )
 
+        create_and_activate_revision(page)
+
         page.save()
         registry[sxp['url'][1:]] = page.id
 
index bd76b22188ea3b1fa50cd03633c7c3f4b9bf61db..6e7c106c3b886128e8366366b9e1f5f834b8504c 100644 (file)
@@ -28,6 +28,7 @@ MIDDLEWARE_CLASSES = [
     'forum.middleware.anon_user.ConnectToSessionMessagesMiddleware',
     'forum.middleware.request_utils.RequestUtils',
     'forum.middleware.cancel.CancelActionMiddleware',
+    'forum.middleware.admin_messages.AdminMessagesMiddleware',
     #'recaptcha_django.middleware.ReCaptchaMiddleware',
     'django.middleware.transaction.TransactionMiddleware',
 ]