summaryrefslogtreecommitdiffstats
path: root/forum
diff options
context:
space:
mode:
Diffstat (limited to 'forum')
-rwxr-xr-xforum/authentication/base.py4
-rwxr-xr-xforum/authentication/forms.py46
-rwxr-xr-xforum/badges/__init__.py10
-rwxr-xr-xforum/badges/base.py11
-rw-r--r--forum/context.py46
-rwxr-xr-xforum/forms.py20
-rwxr-xr-xforum/management/commands/pg_base_command.py35
-rwxr-xr-xforum/management/commands/pg_clean_award_badges.py59
-rwxr-xr-xforum/management/commands/pg_multi_award_badges.py348
-rwxr-xr-xforum/management/commands/pg_once_award_badges.py350
-rwxr-xr-xforum/management/commands/sximport.py109
-rwxr-xr-xforum/models/__init__.py13
-rwxr-xr-xforum/models/answer.py4
-rwxr-xr-xforum/models/base.py11
-rwxr-xr-xforum/models/meta.py6
-rwxr-xr-xforum/models/question.py30
-rwxr-xr-xforum/models/user.py70
-rwxr-xr-xforum/modules.py1
-rwxr-xr-xforum/settings.py51
-rw-r--r--forum/skins/default/media/images/favicon.icobin0 -> 1150 bytes
-rwxr-xr-xforum/skins/default/media/style/style.css3
-rwxr-xr-xforum/skins/default/templates/auth/auth_settings.html35
-rwxr-xr-xforum/skins/default/templates/auth/email_validation.html20
-rwxr-xr-xforum/skins/default/templates/auth/signin.html8
-rwxr-xr-xforum/skins/default/templates/auth/temp_login_email.html20
-rwxr-xr-xforum/skins/default/templates/auth/temp_login_request.html28
-rw-r--r--forum/skins/default/templates/authopenid/changeemail.html88
-rw-r--r--forum/skins/default/templates/authopenid/changeopenid.html35
-rw-r--r--forum/skins/default/templates/authopenid/changepw.html18
-rw-r--r--forum/skins/default/templates/authopenid/complete.html130
-rw-r--r--forum/skins/default/templates/authopenid/confirm_email.txt13
-rw-r--r--forum/skins/default/templates/authopenid/delete.html39
-rw-r--r--forum/skins/default/templates/authopenid/email_validation.txt15
-rw-r--r--forum/skins/default/templates/authopenid/external_legacy_login_info.html15
-rw-r--r--forum/skins/default/templates/authopenid/failure.html14
-rw-r--r--forum/skins/default/templates/authopenid/sendpw.html26
-rw-r--r--forum/skins/default/templates/authopenid/sendpw_email.txt9
-rw-r--r--forum/skins/default/templates/authopenid/settings.html43
-rwxr-xr-xforum/skins/default/templates/authopenid/signin.html186
-rw-r--r--forum/skins/default/templates/authopenid/signup.html32
-rw-r--r--forum/skins/default/templates/authopenid/yadis.xrdf14
-rwxr-xr-xforum/skins/default/templates/base.html2
-rwxr-xr-xforum/skins/default/templates/changepw.html18
-rwxr-xr-xforum/skins/default/templates/email_base.html26
-rwxr-xr-xforum/skins/default/templates/footer.html2
-rwxr-xr-xforum/skins/default/templates/header.html2
-rwxr-xr-xforum/skins/default/templates/index.html47
-rwxr-xr-xforum/skins/default/templates/question.html18
-rwxr-xr-xforum/skins/default/templates/question_list.html42
-rwxr-xr-xforum/skins/default/templates/questions.html101
-rwxr-xr-xforum/skins/default/templates/tag_selector.html2
-rwxr-xr-xforum/skins/default/templates/user_info.html6
-rwxr-xr-xforum/skins/default/templates/user_stats.html2
-rwxr-xr-xforum/skins/default/templates/user_tabs.html14
-rwxr-xr-xforum/skins/default/templates/users.html4
-rwxr-xr-xforum/templatetags/extra_tags.py25
-rwxr-xr-xforum/urls.py15
-rwxr-xr-xforum/user_messages/context_processors.py3
-rwxr-xr-xforum/utils/email.py21
-rwxr-xr-xforum/utils/forms.py4
-rwxr-xr-xforum/utils/lists.py4
-rwxr-xr-xforum/utils/time.py4
-rw-r--r--[-rwxr-xr-x]forum/views/auth.py183
-rw-r--r--forum/views/readers.py1
-rwxr-xr-xforum/views/users.py33
65 files changed, 1625 insertions, 969 deletions
diff --git a/forum/authentication/base.py b/forum/authentication/base.py
index 08534adc..99005866 100755
--- a/forum/authentication/base.py
+++ b/forum/authentication/base.py
@@ -33,6 +33,10 @@ class ConsumerTemplateContext(object):
extra_css = []
show_to_logged_in_user = True
+ @classmethod
+ def readable_key(cls, key):
+ return key.key
+
class InvalidAuthentication(Exception):
def __init__(self, message):
self.message = message
diff --git a/forum/authentication/forms.py b/forum/authentication/forms.py
index 019c85f3..7fa06b01 100755
--- a/forum/authentication/forms.py
+++ b/forum/authentication/forms.py
@@ -1,7 +1,8 @@
-from forum.utils.forms import NextUrlField, UserNameField, UserEmailField
-from forum.models import EmailFeedSetting, Question
+from forum.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
+from forum.models import EmailFeedSetting, Question, User
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
+from django.utils.safestring import mark_safe
from django import forms
from forum.forms import EditUserEmailFeedsForm
import logging
@@ -11,6 +12,29 @@ class SimpleRegistrationForm(forms.Form):
username = UserNameField()
email = UserEmailField()
+class TemporaryLoginRequestForm(forms.Form):
+ def __init__(self, data=None):
+ super(TemporaryLoginRequestForm, self).__init__(data)
+ self.user_cache = None
+
+ email = forms.EmailField(
+ required=True,
+ label=_("Your account email"),
+ error_messages={
+ 'required': _("You cannot leave this field blank"),
+ 'invalid': _('please enter a valid email address'),
+ }
+ )
+
+ def clean_email(self):
+ try:
+ user = User.objects.get(email=self.cleaned_data['email'])
+ except:
+ raise forms.ValidationError(_("Sorry, but this email is not on our database."))
+
+ self.user_cache = user
+ return self.cleaned_data['email']
+
class SimpleEmailSubscribeForm(forms.Form):
SIMPLE_SUBSCRIBE_CHOICES = (
@@ -29,3 +53,21 @@ class SimpleEmailSubscribeForm(forms.Form):
else:
email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
email_settings_form.save(user,save_unbound=True)
+
+class ChangePasswordForm(SetPasswordForm):
+ """ change password form """
+ oldpw = forms.CharField(widget=forms.PasswordInput(attrs={'class':'required'}),
+ label=mark_safe(_('Current password')))
+
+ def __init__(self, data=None, user=None, *args, **kwargs):
+ if user is None:
+ raise TypeError("Keyword argument 'user' must be supplied")
+ super(ChangePasswordForm, self).__init__(data, *args, **kwargs)
+ self.user = user
+
+ def clean_oldpw(self):
+ """ test old password """
+ if not self.user.check_password(self.cleaned_data['oldpw']):
+ raise forms.ValidationError(_("Old password is incorrect. \
+ Please enter the correct password."))
+ return self.cleaned_data['oldpw']
diff --git a/forum/badges/__init__.py b/forum/badges/__init__.py
new file mode 100755
index 00000000..8d7cd097
--- /dev/null
+++ b/forum/badges/__init__.py
@@ -0,0 +1,10 @@
+import re
+
+from forum.badges.base import BadgeImplementation
+from forum.modules import get_modules_script_classes
+
+ALL_BADGES = dict([
+ (re.sub('BadgeImpl', '', name).lower(), cls) for name, cls
+ in get_modules_script_classes('badges', BadgeImplementation).items()
+ if not re.search('AbstractBadgeImpl$', name)
+ ]) \ No newline at end of file
diff --git a/forum/badges/base.py b/forum/badges/base.py
new file mode 100755
index 00000000..03ef3565
--- /dev/null
+++ b/forum/badges/base.py
@@ -0,0 +1,11 @@
+
+
+class BadgeImplementation(object):
+ name = ""
+ description = ""
+
+ def install(self):
+ pass
+
+ def process_job(self):
+ raise NotImplementedError \ No newline at end of file
diff --git a/forum/context.py b/forum/context.py
new file mode 100644
index 00000000..905d24dd
--- /dev/null
+++ b/forum/context.py
@@ -0,0 +1,46 @@
+from django.conf import settings
+def application_settings(context):
+ my_settings = {
+ 'APP_TITLE' : settings.APP_TITLE,
+ 'APP_SHORT_NAME' : settings.APP_SHORT_NAME,
+ 'APP_URL' : settings.APP_URL,
+ 'APP_KEYWORDS' : settings.APP_KEYWORDS,
+ 'APP_DESCRIPTION' : settings.APP_DESCRIPTION,
+ 'APP_INTRO' : settings.APP_INTRO,
+ 'EMAIL_VALIDATION': settings.EMAIL_VALIDATION,
+ 'FEEDBACK_SITE_URL': settings.FEEDBACK_SITE_URL,
+ 'FORUM_SCRIPT_ALIAS': settings.FORUM_SCRIPT_ALIAS,
+ 'LANGUAGE_CODE': settings.LANGUAGE_CODE,
+ 'GOOGLE_SITEMAP_CODE':settings.GOOGLE_SITEMAP_CODE,
+ 'GOOGLE_ANALYTICS_KEY':settings.GOOGLE_ANALYTICS_KEY,
+ 'WIKI_ON':settings.WIKI_ON,
+ 'RESOURCE_REVISION':settings.RESOURCE_REVISION,
+ 'OSQA_SKIN':settings.OSQA_DEFAULT_SKIN,
+ }
+ return {'settings':my_settings}
+
+def auth_processor(request):
+ """
+ Returns context variables required by apps that use Django's authentication
+ system.
+
+ If there is no 'user' attribute in the request, uses AnonymousUser (from
+ django.contrib.auth).
+ """
+ if hasattr(request, 'user'):
+ user = request.user
+ if user.is_authenticated():
+ messages = user.message_set.all()
+ else:
+ messages = None
+ else:
+ from django.contrib.auth.models import AnonymousUser
+ user = AnonymousUser()
+ messages = None
+
+ from django.core.context_processors import PermWrapper
+ return {
+ 'user': user,
+ 'messages': messages,
+ 'perms': PermWrapper(user),
+ }
diff --git a/forum/forms.py b/forum/forms.py
index 2fcbb4a4..6f91acfc 100755
--- a/forum/forms.py
+++ b/forum/forms.py
@@ -262,25 +262,7 @@ class TagFilterSelectionForm(forms.ModelForm):
if before != after:
return True
return False
-
-
-class ChangePasswordForm(SetPasswordForm):
- """ change password form """
- oldpw = forms.CharField(widget=forms.PasswordInput(attrs={'class':'required'}),
- label=mark_safe(_('Current password')))
-
- def __init__(self, data=None, user=None, *args, **kwargs):
- if user is None:
- raise TypeError("Keyword argument 'user' must be supplied")
- super(ChangePasswordForm, self).__init__(data, *args, **kwargs)
- self.user = user
-
- def clean_oldpw(self):
- """ test old password """
- if not self.user.check_password(self.cleaned_data['oldpw']):
- raise forms.ValidationError(_("Old password is incorrect. \
- Please enter the correct password."))
- return self.cleaned_data['oldpw']
+
class EditUserEmailFeedsForm(forms.Form):
WN = (('w',_('weekly')),('n',_('no email')))
diff --git a/forum/management/commands/pg_base_command.py b/forum/management/commands/pg_base_command.py
new file mode 100755
index 00000000..b3167dcf
--- /dev/null
+++ b/forum/management/commands/pg_base_command.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+#encoding:utf-8
+#-------------------------------------------------------------------------------
+# Name: Award badges command
+# Purpose: This is a command file croning in background process regularly to
+# query database and award badges for user's special acitivities.
+#
+# Author: Mike, Sailing
+#
+# Created: 22/01/2009
+# Copyright: (c) Mike 2009
+# Licence: GPL V2
+#-------------------------------------------------------------------------------
+
+from datetime import datetime, date
+from django.core.management.base import NoArgsCommand
+from django.db import connection
+from django.shortcuts import get_object_or_404
+from django.contrib.contenttypes.models import ContentType
+
+from forum.models import *
+from forum.const import *
+
+class BaseCommand(NoArgsCommand):
+ def update_activities_auditted(self, cursor, activity_ids):
+ # update processed rows to auditted
+ if len(activity_ids):
+ query = "UPDATE activity SET is_auditted = TRUE WHERE id in (%s)"\
+ % ','.join('%s' % item for item in activity_ids)
+ cursor.execute(query)
+
+
+
+
+
diff --git a/forum/management/commands/pg_clean_award_badges.py b/forum/management/commands/pg_clean_award_badges.py
new file mode 100755
index 00000000..b3925a68
--- /dev/null
+++ b/forum/management/commands/pg_clean_award_badges.py
@@ -0,0 +1,59 @@
+#-------------------------------------------------------------------------------
+# Name: Award badges command
+# Purpose: This is a command file croning in background process regularly to
+# query database and award badges for user's special acitivities.
+#
+# Author: Mike
+#
+# Created: 18/01/2009
+# Copyright: (c) Mike 2009
+# Licence: GPL V2
+#-------------------------------------------------------------------------------
+#!/usr/bin/env python
+#encoding:utf-8
+from django.core.management.base import NoArgsCommand
+from django.db import connection
+from django.shortcuts import get_object_or_404
+from django.contrib.contenttypes.models import ContentType
+
+from forum.models import *
+
+class Command(NoArgsCommand):
+ def handle_noargs(self, **options):
+ try:
+ try:
+ self.clean_awards()
+ except Exception, e:
+ print e
+ finally:
+ connection.close()
+
+ def clean_awards(self):
+ Award.objects.all().delete()
+
+ award_type =ContentType.objects.get_for_model(Award)
+ Activity.objects.filter(content_type=award_type).delete()
+
+ for user in User.objects.all():
+ user.gold = 0
+ user.silver = 0
+ user.bronze = 0
+ user.save()
+
+ for badge in Badge.objects.all():
+ badge.awarded_count = 0
+ badge.save()
+
+ query = "UPDATE activity SET is_auditted = FALSE"
+ cursor = connection.cursor()
+ try:
+ cursor.execute(query)
+ finally:
+ cursor.close()
+ connection.close()
+
+def main():
+ pass
+
+if __name__ == '__main__':
+ main() \ No newline at end of file
diff --git a/forum/management/commands/pg_multi_award_badges.py b/forum/management/commands/pg_multi_award_badges.py
new file mode 100755
index 00000000..75f84bfe
--- /dev/null
+++ b/forum/management/commands/pg_multi_award_badges.py
@@ -0,0 +1,348 @@
+#!/usr/bin/env python
+#encoding:utf-8
+#-------------------------------------------------------------------------------
+# Name: Award badges command
+# Purpose: This is a command file croning in background process regularly to
+# query database and award badges for user's special acitivities.
+#
+# Author: Mike, Sailing
+#
+# Created: 22/01/2009
+# Copyright: (c) Mike 2009
+# Licence: GPL V2
+#-------------------------------------------------------------------------------
+
+from datetime import datetime, date
+from django.core.management.base import NoArgsCommand
+from django.db import connection
+from django.shortcuts import get_object_or_404
+from django.contrib.contenttypes.models import ContentType
+
+from forum.models import *
+from forum.const import *
+from pg_base_command import BaseCommand
+"""
+(1, '????', 3, '????', '?????3?????????', 1, 0),
+(2, '????', 3, '????', '?????3?????????', 1, 0),
+(3, '????', 3, '????', '????10???', 1, 0),
+(4, '????', 3, '????', '????10???', 1, 0),
+(5, '???', 3, '???', '??10???', 0, 0),
+(6, '????', 3, '????', '????????1000??', 1, 0),
+(7, '???', 3, '???', '?????????', 0, 0),
+(8, '???', 3, '???', '???????', 0, 0),
+(9, '???', 3, '???', '??????', 0, 0),
+(10, '??', 3, '??', '???????', 0, 0),
+(11, '??', 3, '??', '???????', 0, 0),
+(12, '??', 3, '??', '???????', 0, 0),
+(13, '??', 3, '??', '???????????????', 0, 0),
+(14, '???', 3, '???', '??????', 0, 0),
+(15, '??', 3, '??', '??????????????????', 0, 0),
+(16, '????', 3, '????', '????????????', 0, 0),
+(17, '????', 3, '????', '??????????3??????', 1, 0),
+(18, '??????', 1, '??????', '????100????', 1, 0),
+(19, '??????', 1, '??????', '????100????', 1, 0),
+(20, '???', 1, '???', '???100?????', 1, 0),
+(21, '????', 1, '????', '????????10000??', 1, 0),
+(22, 'alpha??', 2, 'alpha??', '?????????', 0, 0),
+(23, '????', 2, '????', '????25????', 1, 0),
+(24, '????', 2, '????', '????25????', 1, 0),
+(25, '?????', 2, '?????', '???25?????', 1, 0),
+(26, '????', 2, '????', '??300???', 0, 0),
+(27, '????', 2, '????', '???100???', 0, 0),
+(28, '??', 2, '??', '?????????', 0, 0),
+(29, '??', 2, '??', '???????????', 0, 0),
+(30, '??', 2, '??', '?????????', 0, 0),
+(31, '??????', 2, '??????', '????????2500??', 1, 0),
+(32, '???', 2, '???', '??????????10???', 0, 0),
+(33, 'beta??', 2, 'beta??', 'beta??????', 0, 0),
+(34, '??', 2, '??', '?????????????40??', 1, 0),
+(35, '??', 2, '??', '???60??????????5???', 1, 0),
+(36, '????', 2, '????', '??????50???????', 1, 0);
+
+
+TYPE_ACTIVITY_ASK_QUESTION=1
+TYPE_ACTIVITY_ANSWER=2
+TYPE_ACTIVITY_COMMENT_QUESTION=3
+TYPE_ACTIVITY_COMMENT_ANSWER=4
+TYPE_ACTIVITY_UPDATE_QUESTION=5
+TYPE_ACTIVITY_UPDATE_ANSWER=6
+TYPE_ACTIVITY_PRIZE=7
+TYPE_ACTIVITY_MARK_ANSWER=8
+TYPE_ACTIVITY_VOTE_UP=9
+TYPE_ACTIVITY_VOTE_DOWN=10
+TYPE_ACTIVITY_CANCEL_VOTE=11
+TYPE_ACTIVITY_DELETE_QUESTION=12
+TYPE_ACTIVITY_DELETE_ANSWER=13
+TYPE_ACTIVITY_MARK_OFFENSIVE=14
+TYPE_ACTIVITY_UPDATE_TAGS=15
+TYPE_ACTIVITY_FAVORITE=16
+TYPE_ACTIVITY_USER_FULL_UPDATED = 17
+"""
+
+class Command(BaseCommand):
+ def handle_noargs(self, **options):
+ try:
+ try:
+ self.delete_question_be_voted_up_3()
+ self.delete_answer_be_voted_up_3()
+ self.delete_question_be_vote_down_3()
+ self.delete_answer_be_voted_down_3()
+ self.answer_be_voted_up_10()
+ self.question_be_voted_up_10()
+ self.question_view_1000()
+ self.answer_self_question_be_voted_up_3()
+ self.answer_be_voted_up_100()
+ self.question_be_voted_up_100()
+ self.question_be_favorited_100()
+ self.question_view_10000()
+ self.answer_be_voted_up_25()
+ self.question_be_voted_up_25()
+ self.question_be_favorited_25()
+ self.question_view_2500()
+ self.answer_be_accepted_and_voted_up_40()
+ self.question_be_answered_after_60_days_and_be_voted_up_5()
+ self.created_tag_be_used_in_question_50()
+ except Exception, e:
+ print e
+ finally:
+ connection.close()
+
+ def delete_question_be_voted_up_3(self):
+ """
+ (1, '????', 3, '????', '?????3?????????', 1, 0),
+ """
+ query = "SELECT act.id, act.user_id, act.object_id FROM activity act, question q WHERE act.object_id = q.id AND\
+ act.activity_type = %s AND\
+ q.vote_up_count >=3 AND \
+ not act.is_auditted" % (TYPE_ACTIVITY_DELETE_QUESTION)
+ self.__process_activities_badge(query, 1, Question)
+
+ def delete_answer_be_voted_up_3(self):
+ """
+ (1, '????', 3, '????', '?????3?????????', 1, 0),
+ """
+ query = "SELECT act.id, act.user_id, act.object_id FROM activity act, answer an WHERE act.object_id = an.id AND\
+ act.activity_type = %s AND\
+ an.vote_up_count >=3 AND \
+ not act.is_auditted" % (TYPE_ACTIVITY_DELETE_ANSWER)
+ self.__process_activities_badge(query, 1, Answer)
+
+ def delete_question_be_vote_down_3(self):
+ """
+ (2, '????', 3, '????', '?????3?????????', 1, 0),
+ """
+ query = "SELECT act.id, act.user_id, act.object_id FROM activity act, question q WHERE act.object_id = q.id AND\
+ act.activity_type = %s AND\
+ q.vote_down_count >=3 AND \
+ not act.is_auditted" % (TYPE_ACTIVITY_DELETE_QUESTION)
+ content_type = ContentType.objects.get_for_model(Question)
+ self.__process_activities_badge(query, 2, Question)
+
+ def delete_answer_be_voted_down_3(self):
+ """
+ (2, '????', 3, '????', '?????3?????????', 1, 0),
+ """
+ query = "SELECT act.id, act.user_id, act.object_id FROM activity act, answer an WHERE act.object_id = an.id AND\
+ act.activity_type = %s AND\
+ an.vote_down_count >=3 AND \
+ not act.is_auditted" % (TYPE_ACTIVITY_DELETE_ANSWER)
+ self.__process_activities_badge(query, 2, Answer)
+
+ def answer_be_voted_up_10(self):
+ """
+ (3, '????', 3, '????', '????10???', 1, 0),
+ """
+ query = "SELECT act.id, act.user_id, act.object_id FROM \
+ activity act, answer a WHERE act.object_id = a.id AND\
+ act.activity_type = %s AND \
+ a.vote_up_count >= 10 AND\
+ not act.is_auditted" % (TYPE_ACTIVITY_ANSWER)
+ self.__process_activities_badge(query, 3, Answer)
+
+ def question_be_voted_up_10(self):
+ """
+ (4, '????', 3, '????', '????10???', 1, 0),
+ """
+ query = "SELECT act.id, act.user_id, act.object_id FROM \
+ activity act, question q WHERE act.object_id = q.id AND\
+ act.activity_type = %s AND \
+ q.vote_up_count >= 10 AND\
+ not act.is_auditted" % (TYPE_ACTIVITY_ASK_QUESTION)
+ self.__process_activities_badge(query, 4, Question)
+
+ def question_view_1000(self):
+ """
+ (6, '????', 3, '????', '????????1000??', 1, 0),
+ """
+ query = "SELECT act.id, act.user_id, act.object_id FROM \
+ activity act, question q WHERE act.activity_type = %s AND\
+ act.object_id = q.id AND \
+ q.view_count >= 1000 AND\
+ act.object_id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 6)
+ self.__process_activities_badge(query, 6, Question, False)
+
+ def answer_self_question_be_voted_up_3(self):
+ """
+ (17, '????', 3, '????', '??????????3??????', 1, 0),
+ """
+ query = "SELECT act.id, act.user_id, act.object_id FROM \
+ activity act, answer an WHERE act.activity_type = %s AND\
+ act.object_id = an.id AND\
+ an.vote_up_count >= 3 AND\
+ act.user_id = (SELECT user_id FROM question q WHERE q.id = an.question_id) AND\
+ act.object_id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 17)
+ self.__process_activities_badge(query, 17, Question, False)
+
+ def answer_be_voted_up_100(self):
+ """
+ (18, '??????', 1, '??????', '????100????', 1, 0),
+ """
+ query = "SELECT an.id, an.author_id FROM answer an WHERE an.vote_up_count >= 100 AND an.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (18)
+
+ self.__process_badge(query, 18, Answer)
+
+ def question_be_voted_up_100(self):
+ """
+ (19, '??????', 1, '??????', '????100????', 1, 0),
+ """
+ query = "SELECT q.id, q.author_id FROM question q WHERE q.vote_up_count >= 100 AND q.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (19)
+
+ self.__process_badge(query, 19, Question)
+
+ def question_be_favorited_100(self):
+ """
+ (20, '???', 1, '???', '???100?????', 1, 0),
+ """
+ query = "SELECT q.id, q.author_id FROM question q WHERE q.favourite_count >= 100 AND q.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (20)
+
+ self.__process_badge(query, 20, Question)
+
+ def question_view_10000(self):
+ """
+ (21, '????', 1, '????', '????????10000??', 1, 0),
+ """
+ query = "SELECT q.id, q.author_id FROM question q WHERE q.view_count >= 10000 AND q.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (21)
+
+ self.__process_badge(query, 21, Question)
+
+ def answer_be_voted_up_25(self):
+ """
+ (23, '????', 2, '????', '????25????', 1, 0),
+ """
+ query = "SELECT a.id, a.author_id FROM answer a WHERE a.vote_up_count >= 25 AND a.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (23)
+
+ self.__process_badge(query, 23, Answer)
+
+ def question_be_voted_up_25(self):
+ """
+ (24, '????', 2, '????', '????25????', 1, 0),
+ """
+ query = "SELECT q.id, q.author_id FROM question q WHERE q.vote_up_count >= 25 AND q.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (24)
+
+ self.__process_badge(query, 24, Question)
+
+ def question_be_favorited_25(self):
+ """
+ (25, '?????', 2, '?????', '???25?????', 1, 0),
+ """
+ query = "SELECT q.id, q.author_id FROM question q WHERE q.favourite_count >= 25 AND q.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (25)
+
+ self.__process_badge(query, 25, Question)
+
+ def question_view_2500(self):
+ """
+ (31, '??????', 2, '??????', '????????2500??', 1, 0),
+ """
+ query = "SELECT q.id, q.author_id FROM question q WHERE q.view_count >= 2500 AND q.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (31)
+
+ self.__process_badge(query, 31, Question)
+
+ def answer_be_accepted_and_voted_up_40(self):
+ """
+ (34, '??', 2, '??', '?????????????40??', 1, 0),
+ """
+ query = "SELECT a.id, a.author_id FROM answer a WHERE a.vote_up_count >= 40 AND\
+ a.accepted AND\
+ a.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (34)
+
+ self.__process_badge(query, 34, Answer)
+
+ def question_be_answered_after_60_days_and_be_voted_up_5(self):
+ """
+ (35, '??', 2, '??', '???60??????????5???', 1, 0),
+ """
+ query = "SELECT a.id, a.author_id FROM question q, answer a WHERE q.id = a.question_id AND\
+ (a.added_at + '60 day'::INTERVAL) >= q.added_at AND\
+ a.vote_up_count >= 5 AND \
+ a.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (35)
+
+ self.__process_badge(query, 35, Answer)
+
+ def created_tag_be_used_in_question_50(self):
+ """
+ (36, '????', 2, '????', '??????50???????', 1, 0);
+ """
+ query = "SELECT t.id, t.created_by_id FROM tag t, auth_user u WHERE t.created_by_id = u.id AND \
+ t. used_count >= 50 AND \
+ t.id NOT IN \
+ (SELECT object_id FROM award WHERE award.badge_id = %s)" % (36)
+
+ self.__process_badge(query, 36, Tag)
+
+ def __process_activities_badge(self, query, badge, content_object, update_auditted=True):
+ content_type = ContentType.objects.get_for_model(content_object)
+
+ cursor = connection.cursor()
+ try:
+ cursor.execute(query)
+ rows = cursor.fetchall()
+
+ if update_auditted:
+ activity_ids = []
+ badge = get_object_or_404(Badge, id=badge)
+ for row in rows:
+ activity_id = row[0]
+ user_id = row[1]
+ object_id = row[2]
+
+ user = get_object_or_404(User, id=user_id)
+ award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
+ award.save()
+
+ if update_auditted:
+ activity_ids.append(activity_id)
+
+ if update_auditted:
+ self.update_activities_auditted(cursor, activity_ids)
+ finally:
+ cursor.close()
+
+ def __process_badge(self, query, badge, content_object):
+ content_type = ContentType.objects.get_for_model(Answer)
+ cursor = connection.cursor()
+ try:
+ cursor.execute(query)
+ rows = cursor.fetchall()
+
+ badge = get_object_or_404(Badge, id=badge)
+ for row in rows:
+ object_id = row[0]
+ user_id = row[1]
+
+ user = get_object_or_404(User, id=user_id)
+ award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
+ award.save()
+ finally:
+ cursor.close()
diff --git a/forum/management/commands/pg_once_award_badges.py b/forum/management/commands/pg_once_award_badges.py
new file mode 100755
index 00000000..b2f79363
--- /dev/null
+++ b/forum/management/commands/pg_once_award_badges.py
@@ -0,0 +1,350 @@
+#!/usr/bin/env python
+#encoding:utf-8
+#-------------------------------------------------------------------------------
+# Name: Award badges command
+# Purpose: This is a command file croning in background process regularly to
+# query database and award badges for user's special acitivities.
+#
+# Author: Mike, Sailing
+#
+# Created: 18/01/2009
+# Copyright: (c) Mike 2009
+# Licence: GPL V2
+#-------------------------------------------------------------------------------
+
+from datetime import datetime, date
+from django.db import connection
+from django.shortcuts import get_object_or_404
+from django.contrib.contenttypes.models import ContentType
+
+from forum.models import *
+from forum.const import *
+from pg_base_command import BaseCommand
+"""
+(1, '????', 3, '????', '?????3?????????', 1, 0),
+(2, '????', 3, '????', '?????3?????????', 1, 0),
+(3, '????', 3, '????', '????10???', 1, 0),
+(4, '????', 3, '????', '????10???', 1, 0),
+(5, '???', 3, '???', '??10???', 0, 0),
+(6, '????', 3, '????', '????????1000??', 1, 0),
+(7, '???', 3, '???', '?????????', 0, 0),
+(8, '???', 3, '???', '???????', 0, 0),
+(9, '???', 3, '???', '??????', 0, 0),
+(10, '??', 3, '??', '???????', 0, 0),
+(11, '??', 3, '??', '???????', 0, 0),
+(12, '??', 3, '??', '???????', 0, 0),
+(13, '??', 3, '??', '???????????????', 0, 0),
+(14, '???', 3, '???', '??????', 0, 0),
+(15, '??', 3, '??', '??????????????????', 0, 0),
+(16, '????', 3, '????', '????????????', 0, 0),
+(17, '????', 3, '????', '??????????3??????', 1, 0),
+(18, '??????', 1, '??????', '????100????', 1, 0),
+(19, '??????', 1, '??????', '????100????', 1, 0),
+(20, '???', 1, '???', '???100?????', 1, 0),
+(21, '????', 1, '????', '????????10000??', 1, 0),
+(22, 'alpha??', 2, 'alpha??', '?????????', 0, 0),
+(23, '????', 2, '????', '????25????', 1, 0),
+(24, '????', 2, '????', '????25????', 1, 0),
+(25, '?????', 2, '?????', '???25?????', 1, 0),
+(26, '????', 2, '????', '??300???', 0, 0),
+(27, '????', 2, '????', '???100???', 0, 0),
+(28, '??', 2, '??', '?????????', 0, 0),
+(29, '??', 2, '??', '???????????', 0, 0),
+(30, '??', 2, '??', '?????????', 0, 0),
+(31, '??????', 2, '??????', '????????2500??', 1, 0),
+(32, '???', 2, '???', '??????????10???', 0, 0),
+(33, 'beta??', 2, 'beta??', 'beta??????', 0, 0),
+(34, '??', 2, '??', '?????????????40??', 1, 0),
+(35, '??', 2, '??', '???60??????????5???', 1, 0),
+(36, '????', 2, '????', '??????50???????', 1, 0);
+
+
+TYPE_ACTIVITY_ASK_QUESTION=1
+TYPE_ACTIVITY_ANSWER=2
+TYPE_ACTIVITY_COMMENT_QUESTION=3
+TYPE_ACTIVITY_COMMENT_ANSWER=4
+TYPE_ACTIVITY_UPDATE_QUESTION=5
+TYPE_ACTIVITY_UPDATE_ANSWER=6
+TYPE_ACTIVITY_PRIZE=7
+TYPE_ACTIVITY_MARK_ANSWER=8
+TYPE_ACTIVITY_VOTE_UP=9
+TYPE_ACTIVITY_VOTE_DOWN=10
+TYPE_ACTIVITY_CANCEL_VOTE=11
+TYPE_ACTIVITY_DELETE_QUESTION=12
+TYPE_ACTIVITY_DELETE_ANSWER=13
+TYPE_ACTIVITY_MARK_OFFENSIVE=14
+TYPE_ACTIVITY_UPDATE_TAGS=15
+TYPE_ACTIVITY_FAVORITE=16
+TYPE_ACTIVITY_USER_FULL_UPDATED = 17
+"""
+
+BADGE_AWARD_TYPE_FIRST = {
+ TYPE_ACTIVITY_MARK_OFFENSIVE : 7,
+ TYPE_ACTIVITY_CANCEL_VOTE: 8,
+ TYPE_ACTIVITY_VOTE_DOWN : 9,
+ TYPE_ACTIVITY_UPDATE_QUESTION : 10,
+ TYPE_ACTIVITY_UPDATE_ANSWER : 10,
+ TYPE_ACTIVITY_UPDATE_TAGS : 11,
+ TYPE_ACTIVITY_MARK_ANSWER : 12,
+ TYPE_ACTIVITY_VOTE_UP : 14,
+ TYPE_ACTIVITY_USER_FULL_UPDATED: 16
+
+}
+
+class Command(BaseCommand):
+ def handle_noargs(self, **options):
+ try:
+ try:
+ self.alpha_user()
+ self.beta_user()
+ self.first_type_award()
+ self.first_ask_be_voted()
+ self.first_answer_be_voted()
+ self.first_answer_be_voted_10()
+ self.vote_count_300()
+ self.edit_count_100()
+ self.comment_count_10()
+ except Exception, e:
+ print e
+ finally:
+ connection.close()
+
+ def alpha_user(self):
+ """
+ Before Jan 25, 2009(Chinese New Year Eve and enter into Beta for CNProg), every registered user
+ will be awarded the "Alpha" badge if he has any activities.
+ """
+ alpha_end_date = date(2009, 1, 25)
+ if date.today() < alpha_end_date:
+ badge = get_object_or_404(Badge, id=22)
+ for user in User.objects.all():
+ award = Award.objects.filter(user=user, badge=badge)
+ if award and not badge.multiple:
+ continue
+ activities = Activity.objects.filter(user=user)
+ if len(activities) > 0:
+ new_award = Award(user=user, badge=badge)
+ new_award.save()
+
+ def beta_user(self):
+ """
+ Before Feb 25, 2009, every registered user
+ will be awarded the "Beta" badge if he has any activities.
+ """
+ beta_end_date = date(2009, 2, 25)
+ if date.today() < beta_end_date:
+ badge = get_object_or_404(Badge, id=33)
+ for user in User.objects.all():
+ award = Award.objects.filter(user=user, badge=badge)
+ if award and not badge.multiple:
+ continue
+ activities = Activity.objects.filter(user=user)
+ if len(activities) > 0:
+ new_award = Award(user=user, badge=badge)
+ new_award.save()
+
+ def first_type_award(self):
+ """
+ This will award below badges for users first behaviors:
+
+ (7, '???', 3, '???', '?????????', 0, 0),
+ (8, '???', 3, '???', '???????', 0, 0),
+ (9, '???', 3, '???', '??????', 0, 0),
+ (10, '??', 3, '??', '???????', 0, 0),
+ (11, '??', 3, '??', '???????', 0, 0),
+ (12, '??', 3, '??', '???????', 0, 0),
+ (14, '???', 3, '???', '??????', 0, 0),
+ (16, '????', 3, '????', '????????????', 0, 0),
+ """
+ activity_types = ','.join('%s' % item for item in BADGE_AWARD_TYPE_FIRST.keys())
+ # ORDER BY user_id, activity_type
+ query = "SELECT id, user_id, activity_type, content_type_id, object_id FROM activity WHERE not is_auditted AND activity_type IN (%s) ORDER BY user_id, activity_type" % activity_types
+
+ cursor = connection.cursor()
+ try:
+ cursor.execute(query)
+ rows = cursor.fetchall()
+ # collect activity_id in current process
+ activity_ids = []
+ last_user_id = 0
+ last_activity_type = 0
+ for row in rows:
+ activity_ids.append(row[0])
+ user_id = row[1]
+ activity_type = row[2]
+ content_type_id = row[3]
+ object_id = row[4]
+
+ # if the user and activity are same as the last, continue
+ if user_id == last_user_id and activity_type == last_activity_type:
+ continue;
+
+ user = get_object_or_404(User, id=user_id)
+ badge = get_object_or_404(Badge, id=BADGE_AWARD_TYPE_FIRST[activity_type])
+ content_type = get_object_or_404(ContentType, id=content_type_id)
+
+ count = Award.objects.filter(user=user, badge=badge).count()
+ if count and not badge.multiple:
+ continue
+ else:
+ # new award
+ award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
+ award.save()
+
+ # set the current user_id and activity_type to last
+ last_user_id = user_id
+ last_activity_type = activity_type
+
+ # update processed rows to auditted
+ self.update_activities_auditted(cursor, activity_ids)
+ finally:
+ cursor.close()
+
+ def first_ask_be_voted(self):
+ """
+ For user asked question and got first upvote, we award him following badge:
+
+ (13, '??', 3, '??', '???????????????', 0, 0),
+ """
+ query = "SELECT act.user_id, q.vote_up_count, act.object_id FROM " \
+ "activity act, question q WHERE act.activity_type = %s AND " \
+ "act.object_id = q.id AND " \
+ "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 13)
+ cursor = connection.cursor()
+ try:
+ cursor.execute(query)
+ rows = cursor.fetchall()
+
+ badge = get_object_or_404(Badge, id=13)
+ content_type = ContentType.objects.get_for_model(Question)
+ awarded_users = []
+ for row in rows:
+ user_id = row[0]
+ vote_up_count = row[1]
+ object_id = row[2]
+ if vote_up_count > 0 and user_id not in awarded_users:
+ user = get_object_or_404(User, id=user_id)
+ award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
+ award.save()
+ awarded_users.append(user_id)
+ finally:
+ cursor.close()
+
+ def first_answer_be_voted(self):
+ """
+ When user answerd questions and got first upvote, we award him following badge:
+
+ (15, '??', 3, '??', '??????????????????', 0, 0),
+ """
+ query = "SELECT act.user_id, a.vote_up_count, act.object_id FROM " \
+ "activity act, answer a WHERE act.activity_type = %s AND " \
+ "act.object_id = a.id AND " \
+ "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 15)
+ cursor = connection.cursor()
+ try:
+ cursor.execute(query)
+ rows = cursor.fetchall()
+
+ awarded_users = []
+ badge = get_object_or_404(Badge, id=15)
+ content_type = ContentType.objects.get_for_model(Answer)
+ for row in rows:
+ user_id = row[0]
+ vote_up_count = row[1]
+ object_id = row[2]
+ if vote_up_count > 0 and user_id not in awarded_users:
+ user = get_object_or_404(User, id=user_id)
+ award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
+ award.save()
+ awarded_users.append(user_id)
+ finally:
+ cursor.close()
+
+ def first_answer_be_voted_10(self):
+ """
+ (32, '???', 2, '???', '??????????10???', 0, 0)
+ """
+ query = "SELECT act.user_id, act.object_id FROM " \
+ "activity act, answer a WHERE act.object_id = a.id AND " \
+ "act.activity_type = %s AND " \
+ "a.vote_up_count >= 10 AND " \
+ "act.user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 32)
+ cursor = connection.cursor()
+ try:
+ cursor.execute(query)
+ rows = cursor.fetchall()
+
+ awarded_users = []
+ badge = get_object_or_404(Badge, id=32)
+ content_type = ContentType.objects.get_for_model(Answer)
+ for row in rows:
+ user_id = row[0]
+ if user_id not in awarded_users:
+ user = get_object_or_404(User, id=user_id)
+ object_id = row[1]
+ award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
+ award.save()
+ awarded_users.append(user_id)
+ finally:
+ cursor.close()
+
+ def vote_count_300(self):
+ """
+ (26, '????', 2, '????', '??300???', 0, 0)
+ """
+ query = "SELECT count(*) as vote_count, user_id FROM activity WHERE " \
+ "activity_type = %s OR " \
+ "activity_type = %s AND " \
+ "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
+ "GROUP BY user_id HAVING count(*) >= 300" % (TYPE_ACTIVITY_VOTE_UP, TYPE_ACTIVITY_VOTE_DOWN, 26)
+
+ self.__award_for_count_num(query, 26)
+
+ def edit_count_100(self):
+ """
+ (27, '????', 2, '????', '???100???', 0, 0)
+ """
+ query = "SELECT count(*) as vote_count, user_id FROM activity WHERE " \
+ "activity_type = %s OR " \
+ "activity_type = %s AND " \
+ "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
+ "GROUP BY user_id HAVING count(*) >= 100" % (TYPE_ACTIVITY_UPDATE_QUESTION, TYPE_ACTIVITY_UPDATE_ANSWER, 27)
+
+ self.__award_for_count_num(query, 27)
+
+ def comment_count_10(self):
+ """
+ (5, '???', 3, '???', '??10???', 0, 0),
+ """
+ query = "SELECT count(*) as vote_count, user_id FROM activity WHERE " \
+ "activity_type = %s OR " \
+ "activity_type = %s AND " \
+ "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
+ "GROUP BY user_id HAVING count(*) >= 10" % (TYPE_ACTIVITY_COMMENT_QUESTION, TYPE_ACTIVITY_COMMENT_ANSWER, 5)
+ self.__award_for_count_num(query, 5)
+
+ def __award_for_count_num(self, query, badge):
+ cursor = connection.cursor()
+ try:
+ cursor.execute(query)
+ rows = cursor.fetchall()
+
+ awarded_users = []
+ badge = get_object_or_404(Badge, id=badge)
+ for row in rows:
+ vote_count = row[0]
+ user_id = row[1]
+
+ if user_id not in awarded_users:
+ user = get_object_or_404(User, id=user_id)
+ award = Award(user=user, badge=badge)
+ award.save()
+ awarded_users.append(user_id)
+ finally:
+ cursor.close()
+
+def main():
+ pass
+
+if __name__ == '__main__':
+ main()
diff --git a/forum/management/commands/sximport.py b/forum/management/commands/sximport.py
new file mode 100755
index 00000000..f6af4e90
--- /dev/null
+++ b/forum/management/commands/sximport.py
@@ -0,0 +1,109 @@
+from django.core.management.base import LabelCommand
+from zipfile import ZipFile
+from xml.dom import minidom as dom
+import datetime
+
+from forum.models import User
+
+class Command(LabelCommand):
+ def handle_label(self, label, **options):
+ zip = ZipFile(label)
+
+ map = {}
+
+ map['users'] = self.import_users(zip.open("Users.xml"))
+ map['questions'], map['answers'] = self.import_posts(zip.open("Posts.xml"))
+
+
+ def row_to_dic(self, row):
+ return dict([
+ (child.localName.lower(),
+ " ".join([t.nodeValue for t in child.childNodes if t.nodeType == t.TEXT_NODE]))
+ for child in row.childNodes
+ if child.nodeType == child.ELEMENT_NODE
+ ])
+
+ def from_sx_time(self, timestring):
+ if timestring is None:
+ return timestring
+
+ try:
+ return datetime.datetime.strptime(timestring, '%Y-%m-%dT%H:%M:%S')
+ except:
+ return datetime.datetime.strptime(timestring, '%Y-%m-%dT%H:%M:%S.%f')
+
+
+ def import_users(self, users):
+ pkey_map = {}
+ doc = dom.parse(users)
+
+ rows = doc.getElementsByTagName('row')
+ unknown_count = 0
+
+ added_names = []
+
+ for row in rows:
+ values = self.row_to_dic(row)
+
+ username = values.get('displayname',
+ values.get('realname',
+ values.get('email', None)))
+
+ if username is None:
+ unknown_count += 1
+ username = 'Unknown User %d' % unknown_count
+
+ if username in added_names:
+ cnt = 1
+ new_username = "%s %d" % (username, cnt)
+ while new_username in added_names:
+ cnt += 1
+ new_username = "%s %d" % (username, cnt)
+
+ username = new_username
+
+ added_names.append(username)
+
+ user = User(username=username, email=values.get('email', ''))
+
+ user.reputation = values['reputation']
+ user.last_seen = self.from_sx_time(values['lastaccessdate'])
+
+ user.real_name = values.get('realname', '')
+ user.about = values.get('aboutme', '')
+ user.website = values.get('websiteurl', '')
+ user.date_of_birth = self.from_sx_time(values.get('birthday', None))
+ user.location = values.get('location', '')
+
+ user.is_active = True
+ user.email_isvalid = True
+
+
+ if int(values['usertypeid']) == 5:
+ user.is_superuser = True
+
+ if int(values['usertypeid']) == 5:
+ user.is_staff = True
+
+ user.save()
+
+ pkey_map[values['id']] = user
+
+ return users
+
+ def import_posts(self, posts, map):
+ pkey_map = {}
+ doc = dom.parse(posts)
+
+ rows = doc.getElementsByTagName('row')
+
+ for row in rows:
+ map = {
+ 'title': row['']
+ }
+
+ pass
+ pass
+
+
+
diff --git a/forum/models/__init__.py b/forum/models/__init__.py
index ddb21319..f4850025 100755
--- a/forum/models/__init__.py
+++ b/forum/models/__init__.py
@@ -2,7 +2,7 @@ from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion
from answer import Answer, AnonymousAnswer, AnswerRevision
from tag import Tag, MarkedTag
from meta import Vote, Comment, FlaggedItem
-from user import Activity, AnonymousEmail, EmailFeedSetting, AuthKeyUserAssociation
+from user import Activity, ValidationHash, EmailFeedSetting, AuthKeyUserAssociation
from repute import Badge, Award, Repute
import re
@@ -48,6 +48,9 @@ def user_get_q_sel_email_feed_frequency(self):
#print 'have freq=%s' % feed_setting.frequency
return feed_setting.frequency
+def user_get_absolute_url(self):
+ return "/users/%d/%s/" % (self.id, (self.username))
+
User.add_to_class('is_approved', models.BooleanField(default=False))
User.add_to_class('email_isvalid', models.BooleanField(default=False))
User.add_to_class('email_key', models.CharField(max_length=32, null=True))
@@ -82,6 +85,7 @@ User.add_to_class('tag_filter_setting',
default='ignored'
)
)
+User.add_to_class('get_absolute_url', user_get_absolute_url)
def get_messages(self):
messages = []
@@ -94,7 +98,7 @@ def delete_messages(self):
def get_profile_url(self):
"""Returns the URL for this User's profile."""
- return '%s%s/' % (reverse('user', args=[self.id]), slugify(self.username))
+ return "/users/%d/%s" % (self.id, slugify(self.username))
def get_profile_link(self):
profile_link = u'<a href="%s">%s</a>' % (self.get_profile_url(),self.username)
@@ -413,7 +417,6 @@ def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs)
aa.publish(user)
#signal for User modle save changes
-
pre_save.connect(calculate_gravatar_hash, sender=User)
post_save.connect(record_ask_event, sender=Question)
post_save.connect(record_answer_event, sender=Answer)
@@ -457,7 +460,7 @@ Repute = Repute
Activity = Activity
EmailFeedSetting = EmailFeedSetting
-AnonymousEmail = AnonymousEmail
+ValidationHash = ValidationHash
AuthKeyUserAssociation = AuthKeyUserAssociation
__all__ = [
@@ -483,7 +486,7 @@ __all__ = [
'Activity',
'EmailFeedSetting',
- 'AnonymousEmail',
+ 'ValidationHash',
'AuthKeyUserAssociation',
'User',
diff --git a/forum/models/answer.py b/forum/models/answer.py
index e7847f32..3fd08d98 100755
--- a/forum/models/answer.py
+++ b/forum/models/answer.py
@@ -10,7 +10,7 @@ markdowner = Markdown(html4tags=True)
from question import Question
class AnswerManager(models.Manager):
- def create_new(cls, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False):
+ def create_new(self, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False):
answer = Answer(
question = question,
author = author,
@@ -177,7 +177,7 @@ class AnonymousAnswer(AnonymousContent):
def publish(self,user):
added_at = datetime.datetime.now()
#print user.id
- AnswerManager.create_new(question=self.question,wiki=self.wiki,
+ Answer.objects.create_new(question=self.question,wiki=self.wiki,
added_at=added_at,text=self.text,
author=user)
self.delete()
diff --git a/forum/models/base.py b/forum/models/base.py
index 56cb95be..fb66ff1b 100755
--- a/forum/models/base.py
+++ b/forum/models/base.py
@@ -17,11 +17,15 @@ import django.dispatch
from django.conf import settings
import logging
-if settings.USE_SPHINX_SEARCH == True:
- from djangosphinx.models import SphinxSearch
-
from forum.const import *
+class UserContent(models.Model):
+ user = models.ForeignKey(User, related_name='%(class)ss')
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
class MetaContent(models.Model):
"""
Base class for Vote, Comment and FlaggedItem
@@ -29,7 +33,6 @@ class MetaContent(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
- user = models.ForeignKey(User, related_name='%(class)ss')
class Meta:
abstract = True
diff --git a/forum/models/meta.py b/forum/models/meta.py
index 6923a932..114d2130 100755
--- a/forum/models/meta.py
+++ b/forum/models/meta.py
@@ -22,7 +22,7 @@ class VoteManager(models.Manager):
return 0
-class Vote(MetaContent):
+class Vote(MetaContent, UserContent):
VOTE_UP = +1
VOTE_DOWN = -1
VOTE_CHOICES = (
@@ -61,7 +61,7 @@ class FlaggedItemManager(models.Manager):
else:
return 0
-class FlaggedItem(MetaContent):
+class FlaggedItem(MetaContent, UserContent):
"""A flag on a Question or Answer indicating offensive content."""
flagged_at = models.DateTimeField(default=datetime.datetime.now)
@@ -74,7 +74,7 @@ class FlaggedItem(MetaContent):
def __unicode__(self):
return '[%s] flagged at %s' %(self.user, self.flagged_at)
-class Comment(MetaContent):
+class Comment(MetaContent, UserContent):
comment = models.CharField(max_length=300)
added_at = models.DateTimeField(default=datetime.datetime.now)
diff --git a/forum/models/question.py b/forum/models/question.py
index 68cd5293..280c87c0 100755
--- a/forum/models/question.py
+++ b/forum/models/question.py
@@ -7,6 +7,8 @@ from django.utils.html import strip_tags
import datetime
markdowner = Markdown(html4tags=True)
+from forum.utils.lists import LazyList
+
class QuestionManager(models.Manager):
def create_new(cls, title=None,author=None,added_at=None, wiki=False,tagnames=None, text=None):
html = sanitize_html(markdowner.convert(text))
@@ -104,17 +106,25 @@ class QuestionManager(models.Manager):
Questions with the individual tags will be added to list if above questions are not full.
"""
#print datetime.datetime.now()
- questions = list(self.filter(tagnames = question.tagnames, deleted=False).all())
- tags_list = question.tags.all()
- for tag in tags_list:
- extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50]
- for item in extend_questions:
- if item not in questions and len(questions) < 10:
- questions.append(item)
+ manager = self
+
+ def get_data():
+ questions = list(manager.filter(tagnames = question.tagnames, deleted=False).all())
+
+ tags_list = question.tags.all()
+ for tag in tags_list:
+ extend_questions = manager.filter(tags__id = tag.id, deleted=False)[:50]
+ for item in extend_questions:
+ if item not in questions and len(questions) < 10:
+ questions.append(item)
+
+ #print datetime.datetime.now()
+ return questions
+
+ return LazyList(get_data)
+
- #print datetime.datetime.now()
- return questions
class Question(Content, DeletableContent):
title = models.CharField(max_length=300)
@@ -432,7 +442,7 @@ class AnonymousQuestion(AnonymousContent):
def publish(self,user):
added_at = datetime.datetime.now()
- QuestionManager.create_new(title=self.title, author=user, added_at=added_at,
+ Question.objects.create_new(title=self.title, author=user, added_at=added_at,
wiki=self.wiki, tagnames=self.tagnames,
summary=self.summary, text=self.text)
self.delete()
diff --git a/forum/models/user.py b/forum/models/user.py
index 8db119e0..6d871bf4 100755
--- a/forum/models/user.py
+++ b/forum/models/user.py
@@ -1,6 +1,9 @@
from base import *
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
+from hashlib import md5
+import string
+from random import Random
from django.utils.translation import ugettext as _
@@ -57,19 +60,74 @@ class EmailFeedSetting(models.Model):
class Meta:
app_label = 'forum'
-class AnonymousEmail(models.Model):
- #validation key, if used
- key = models.CharField(max_length=32)
- email = models.EmailField(null=False,unique=True)
- isvalid = models.BooleanField(default=False)
+from forum.utils.time import one_day_from_now
+
+class ValidationHashManager(models.Manager):
+ def _generate_md5_hash(self, user, type, hash_data, seed):
+ return md5("%s%s%s%s" % (seed, "".join(map(str, hash_data)), user.id, type)).hexdigest()
+
+ def create_new(self, user, type, hash_data=[], expiration=None):
+ seed = ''.join(Random().sample(string.letters+string.digits, 12))
+ hash = self._generate_md5_hash(user, type, hash_data, seed)
+
+ obj = ValidationHash(hash_code=hash, seed=seed, user=user, type=type)
+
+ if expiration is not None:
+ obj.expiration = expiration
+
+ try:
+ obj.save()
+ except:
+ return None
+
+ return obj
+
+ def validate(self, hash, user, type, hash_data=[]):
+ try:
+ obj = self.get(hash_code=hash)
+ except:
+ return False
+
+ if obj.type != type:
+ return False
+
+ if obj.user != user:
+ return False
+
+ valid = (obj.hash_code == self._generate_md5_hash(obj.user, type, hash_data, obj.seed))
+
+ if valid:
+ if obj.expiration < datetime.datetime.now():
+ obj.delete()
+ return False
+ else:
+ obj.delete()
+ return True
+
+ return False
+
+class ValidationHash(models.Model):
+ #todo: was 256 chars - is that important?
+ #on mysql 255 is max for unique=True
+ hash_code = models.CharField(max_length=255,unique=True)
+ seed = models.CharField(max_length=12)
+ expiration = models.DateTimeField(default=one_day_from_now)
+ type = models.CharField(max_length=12)
+ user = models.ForeignKey(User)
+
+ objects = ValidationHashManager()
class Meta:
+ unique_together = ('user', 'type')
app_label = 'forum'
+ def __str__(self):
+ return self.hash_code
+
class AuthKeyUserAssociation(models.Model):
key = models.CharField(max_length=255,null=False,unique=True)
provider = models.CharField(max_length=64)#string 'yahoo', 'google', etc.
- user = models.ForeignKey(User)
+ user = models.ForeignKey(User, related_name="auth_keys")
added_at = models.DateTimeField(default=datetime.datetime.now)
class Meta:
diff --git a/forum/modules.py b/forum/modules.py
index f786c719..6c9a9dba 100755
--- a/forum/modules.py
+++ b/forum/modules.py
@@ -54,7 +54,6 @@ def get_all_handlers(name):
def get_handler(name, default):
all = get_all_handlers(name)
- print(len(all))
return len(all) and all[0] or default
module_template_re = re.compile('^modules\/(\w+)\/(.*)$')
diff --git a/forum/settings.py b/forum/settings.py
new file mode 100755
index 00000000..04a7c399
--- /dev/null
+++ b/forum/settings.py
@@ -0,0 +1,51 @@
+import os
+
+
+INSTALLED_APPS = ['forum']
+
+MIDDLEWARE_CLASSES = [
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'forum.middleware.anon_user.ConnectToSessionMessagesMiddleware',
+ 'forum.middleware.pagesize.QuestionsPageSizeMiddleware',
+ 'forum.middleware.cancel.CancelActionMiddleware',
+ 'django.middleware.transaction.TransactionMiddleware',
+]
+
+TEMPLATE_LOADERS = [
+ 'django.template.loaders.filesystem.load_template_source',
+ 'django.template.loaders.app_directories.load_template_source',
+ 'forum.modules.module_templates_loader',
+ 'forum.skins.load_template_source',
+]
+
+TEMPLATE_CONTEXT_PROCESSORS = [
+ 'django.core.context_processors.request',
+ 'forum.context.application_settings',
+ 'forum.user_messages.context_processors.user_messages',
+ 'django.core.context_processors.auth',
+]
+
+TEMPLATE_DIRS = [
+ os.path.join(os.path.dirname(__file__),'skins').replace('\\','/'),
+]
+
+def setup_settings(settings):
+
+ if (hasattr(settings, 'DEBUG') and getattr(settings, 'DEBUG')):
+ try:
+ import debug_toolbar
+ INSTALLED_APPS.append('debug_toolbar')
+ MIDDLEWARE_CLASSES.append('debug_toolbar.middleware.DebugToolbarMiddleware')
+ except:
+ pass
+
+
+ settings.INSTALLED_APPS = set(settings.INSTALLED_APPS) | set(INSTALLED_APPS)
+ settings.MIDDLEWARE_CLASSES = set(settings.MIDDLEWARE_CLASSES) | set(MIDDLEWARE_CLASSES)
+ settings.TEMPLATE_LOADERS = set(settings.TEMPLATE_LOADERS) | set(TEMPLATE_LOADERS)
+ settings.TEMPLATE_CONTEXT_PROCESSORS = set(settings.TEMPLATE_CONTEXT_PROCESSORS) | set(TEMPLATE_CONTEXT_PROCESSORS)
+ settings.TEMPLATE_DIRS = set(settings.TEMPLATE_DIRS) | set(TEMPLATE_DIRS)
+
+ \ No newline at end of file
diff --git a/forum/skins/default/media/images/favicon.ico b/forum/skins/default/media/images/favicon.ico
new file mode 100644
index 00000000..35c9e149
--- /dev/null
+++ b/forum/skins/default/media/images/favicon.ico
Binary files differ
diff --git a/forum/skins/default/media/style/style.css b/forum/skins/default/media/style/style.css
index 02148ab0..4f6b366b 100755
--- a/forum/skins/default/media/style/style.css
+++ b/forum/skins/default/media/style/style.css
@@ -1,8 +1,5 @@
@import url(jquery.autocomplete.css);
-@import url(openid.css);
-@import url(prettify.css);
-/* 公用 */
body {
background: #FFF;
font-size: 12px;
diff --git a/forum/skins/default/templates/auth/auth_settings.html b/forum/skins/default/templates/auth/auth_settings.html
new file mode 100755
index 00000000..051fb6ba
--- /dev/null
+++ b/forum/skins/default/templates/auth/auth_settings.html
@@ -0,0 +1,35 @@
+{% extends "base.html" %}
+<!-- changepw.html -->
+{% load i18n %}
+{% block head %}{% endblock %}
+{% block title %}{% spaceless %}{% trans "Authentication settings" %}{% endspaceless %}{% endblock %}
+{% block content %}
+<div class="headNormal">{% trans "Authentication settings" %}</div>
+{% if auth_keys %}
+ <p class="message">{% blocktrans %}These are the external authentication providers currently associated with your account.{% endblocktrans %}</p>
+ <div>
+ {% for key in auth_keys %}
+ <p>{{ key.name }} (<a href="{% url user_remove_external_provider id=key.id %}">{% trans "remove" %}</a>)</p>
+ {% endfor %}
+ </div>
+{% endif %}
+{% if not auth_keys %}
+ <p class="message">{% blocktrans %}You currently have no external authentication provider associated with your account.{% endblocktrans %}</p>
+{% endif %}
+<input type="button" class="submit" value="{% trans "Add new provider" %}" onclick="window.location='{% url user_add_external_provider %}'" />
+{% if has_password %}
+ <p class="message">{% blocktrans %}This is where you can change your password. Make sure you remember it!{% endblocktrans %}</p>
+{% endif %}
+{% if not has_password %}
+ <p class="message">{% blocktrans %}You can set up a password for your account, so you can login using standard username and password!{% endblocktrans %}</p>
+{% endif %}
+<div class="aligned">
+ <form action="" method="post" accept-charset="utf-8">
+ <ul id="changepw-form" class="form-horizontal-rows">
+ {{form.as_ul}}
+ </ul>
+ <div class="submit-row"><input type="submit" class="submit" value="{% if has_password %}{% trans "Change password" %}{% endif %}{% if not has_password %}{% trans "Create password" %}{% endif %}" /></div>
+ </form>
+ </div>
+{% endblock %}
+<!-- end changepw.html -->
diff --git a/forum/skins/default/templates/auth/email_validation.html b/forum/skins/default/templates/auth/email_validation.html
new file mode 100755
index 00000000..a4126a69
--- /dev/null
+++ b/forum/skins/default/templates/auth/email_validation.html
@@ -0,0 +1,20 @@
+{% extends "email_base.html" %}
+{% load i18n %}
+{% load extra_tags %}
+
+{% block content %}
+ <p>{% trans "Greetings from the Q&A forum" %},</p>
+
+ <p>{% trans "To make use of the Forum, please follow the link below:" %}</p>
+
+ <a href="{% fullurl auth_validate_email user=user.id,code=validation_code %}">{% fullurl auth_validate_email user=user.id,code=validation_code %}</a>
+
+ <p>{% trans "Following the link above will help us verify your email address." %}</p>
+
+ <p>{% blocktrans %}If you beleive that this message was sent in mistake -
+ no further action is needed. Just ingore this email, we apologize
+ for any inconvenience{% endblocktrans %}</p>
+
+ <p>{% blocktrans %}Sincerely,<br />
+ Forum Administrator{% endblocktrans %}</p>
+{% endblock %}
diff --git a/forum/skins/default/templates/auth/signin.html b/forum/skins/default/templates/auth/signin.html
index d4ee9fc1..78e6c76d 100755
--- a/forum/skins/default/templates/auth/signin.html
+++ b/forum/skins/default/templates/auth/signin.html
@@ -31,7 +31,7 @@
{% if request.user.is_anonymous %}
<div style="width:600px;float:left;margin-bottom:5px;">
<input type="checkbox" checked="checked" id="validate_email" />
- {% trans "Take the opurtunity to validate my email next to the external provider I choose." %}
+ {% trans "Take the oppurtunity to validate my email next to the external provider I choose." %}
</div>
{% endif %}
<div id="bigicon_providers">
@@ -73,12 +73,16 @@
<input type="hidden" class="validate_email" name="validate_email" value="yes" />
</form>
{% for provider in stackitem_providers %}
- <h3 class="or_label">Or...</h3>
+ <h3 class="or_label">{% trans 'Or...' %}</h3>
<form class="signin_form" method="POST" action="{% url auth_provider_signin provider=provider.id %}">
{% include provider.stack_item_template %}
<input type="hidden" class="validate_email" name="validate_email" value="yes" />
</form>
{% endfor %}
+ <h3 class="or_label">{% trans 'Or...' %}</h3>
+ <fieldset>
+ {% trans 'Click' %} <a href="{% url auth_request_tempsignin %}">here</a> {% trans "if you're having troubles signing in." %}
+ </fieldset>
<script type="text/html" id="simple_form_template">
<fieldset id="slot_form">
<p id="provider_name_slot">{% trans 'Enter your ' %}%%YOUR_WHAT%%</p>
diff --git a/forum/skins/default/templates/auth/temp_login_email.html b/forum/skins/default/templates/auth/temp_login_email.html
new file mode 100755
index 00000000..063608fe
--- /dev/null
+++ b/forum/skins/default/templates/auth/temp_login_email.html
@@ -0,0 +1,20 @@
+{% extends "email_base.html" %}
+{% load i18n %}
+{% load extra_tags %}
+
+{% block content %}
+ <p>{% trans "Greetings from the Q&A forum" %},</p>
+
+ <p>{% trans "You're seeing this because someone requested a temporary login link" %}</p>
+
+ <a href="{% fullurl auth_tempsignin user=user.id,code=temp_login_code %}">{% fullurl auth_tempsignin user=user.id,code=temp_login_code %}</a>
+
+ <p>{% trans "Following the link above will give you access to your account." %}</p>
+
+ <p>{% blocktrans %}If you beleive that this message was sent in mistake -
+ no further action is needed. Just ingore this email, we apologize
+ for any inconvenience{% endblocktrans %}</p>
+
+ <p>{% blocktrans %}Sincerely,<br />
+ Forum Administrator{% endblocktrans %}</p>
+{% endblock %}
diff --git a/forum/skins/default/templates/auth/temp_login_request.html b/forum/skins/default/templates/auth/temp_login_request.html
new file mode 100755
index 00000000..772f18fb
--- /dev/null
+++ b/forum/skins/default/templates/auth/temp_login_request.html
@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+
+{% load i18n %}
+{% block head %}{% endblock %}
+{% block title %}{% spaceless %}{% trans "Request temporary login key" %}{% endspaceless %}{% endblock %}
+{% block content %}
+<div class="headNormal">{% trans "Account: request temporary login key" %}</div>
+<p class="message">{% blocktrans %}
+ If you're experiencing problems accessing your account, or if you forgot your password,
+ here you can request a temporary login key. Fill out your account email and we'll send you a temporary access link that
+ will enable you to access your account. This token is valid only once and for a limited period of time.
+ {% endblocktrans %}</p>
+<div class="aligned">
+ {% if form.errors %}
+ <ul class="errorlist">
+ {% for error in form.errors %}
+ <li>{{ error }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ <form action="" method="post" accept-charset="utf-8">
+ <ul id="changepw-form" class="form-horizontal-rows">
+ {{form.as_ul}}
+ </ul>
+ <div class="submit-row"><input type="submit" class="submit" value="{% trans "Send link" %}" /></div>
+ </form>
+ </div>
+{% endblock %} \ No newline at end of file
diff --git a/forum/skins/default/templates/authopenid/changeemail.html b/forum/skins/default/templates/authopenid/changeemail.html
deleted file mode 100644
index 94d1881c..00000000
--- a/forum/skins/default/templates/authopenid/changeemail.html
+++ /dev/null
@@ -1,88 +0,0 @@
-{% extends "base_content.html" %}
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Change email" %}{% endspaceless %}{% endblock %}
-{% block content %}
-<!-- changeemail.html action_type={{action_type}}-->
-{% ifequal action_type "change" %}
- <div id="main-bar" class="headNormal">
- {% if user.email %}
- {% trans "Change email" %}
- {% else %}
- {% trans "Save your email address" %}
- {% endif %}
- </div>
- <p class="message">
- {% if user.email %}
- {% blocktrans %}change {{email}} info{% endblocktrans %}
- {% else %}
- {% blocktrans %}here is why email is required, see {{gravatar_faq_url}}{% endblocktrans %}
- {% endif %}
- </p>
- {% if msg %}
- <p class="error">{{ msg }}</p>
- {% endif %}
-
- <div class="aligned">
- <form action="." method="post" accept-charset="utf-8">
- {% if next %}
- <input type="hidden" name="next" value="{{next}}"/>
- {% endif %}
- <div class="form-row-vertical">
- <label for="id_email">{% if user.email %}{% trans "Your new Email" %}{% else %}{% trans "Your Email" %}{% endif %}</label>
- {% if form.email.errors %}
- <p class="error">{{form.email.errors|join:", "}}</p>
- {% endif %}
- {{ form.email }}
- </div>
- <div class="submit-row">
- <input class="submit" type="submit" name="change_email" value="{% if user.email %}{% trans "Change email" %}{% else %}{% trans "Save Email" %}{% endif %}">
- {% if user.email %}
- <input class="submit" type="submit" name="cancel" value="{% trans "Cancel" %}">
- {% endif %}
- </div>
-
- </form>
- </div>
-{% endifequal %}
-{% ifequal action_type "validate" %}
- <div id="main-bar" class="headNormal">
- {% trans "Validate email" %}
- </div>
- <p class="message">
- {% blocktrans %}validate {{email}} info or go to {{change_email_url}}{% endblocktrans %}
- </p>
-{% endifequal %}
-{% ifequal action_type "keep" %}
- <div id="main-bar" class="headNormal">
- {% trans "Email not changed" %}
- </div>
- <p class="message">
- {% blocktrans %}old {{email}} kept, if you like go to {{change_email_url}}{% endblocktrans %}
- </p>
-{% endifequal %}
-{% ifequal action_type "done_novalidate" %}
- <div id="main-bar" class="headNormal">
- {% trans "Email changed" %}
- </div>
- <p class="message">
- {% blocktrans %}your current {{email}} can be used for this{% endblocktrans %}
- </p>
-{% endifequal %}
-{% ifequal action_type "validation_complete" %}
- <div id="main-bar" class="headNormal">
- {% trans "Email verified" %}
- </div>
- <p class="message">
- {% trans "thanks for verifying email" %}
- </p>
-{% endifequal %}
-{% ifequal action_type "key_not_sent" %}
- <div id="main-bar" class="headNormal">
- {% trans "email key not sent" %}
- </div>
- <p class="message">
- {% blocktrans %}email key not sent {{email}} change email here {{change_link}}{% endblocktrans %}
- </p>
-{% endifequal %}
-{% endblock %}
-<!-- end changeemail.html -->
diff --git a/forum/skins/default/templates/authopenid/changeopenid.html b/forum/skins/default/templates/authopenid/changeopenid.html
deleted file mode 100644
index d01788fb..00000000
--- a/forum/skins/default/templates/authopenid/changeopenid.html
+++ /dev/null
@@ -1,35 +0,0 @@
-{% extends "base.html" %}
-<!-- changeopenid.html -->
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Change OpenID" %}{% endspaceless %}{% endblock %}
-{% block content %}
-<div id="main-bar" class="">
- <h3>
- {% trans "Account: change OpenID URL" %}
- </h3>
-</div>
-
-<p>{% blocktrans %}This is where you can change your OpenID URL. Make sure you remember it!{% endblocktrans %}</p>
-{% if form.errors %}
-<p class="errors">{% trans "Please correct errors below:" %}<br />
- {% if form.openid_url.errors %}
- <span class="error">{{ form.openid_url.errors|join:", " }}</span>
- {% endif %}
-
-
-</p>
-{% endif %}
-{% if msg %}
- <p class="errors">{{ msg }}</p>
-{% endif %}
-
-<div class="aligned">
- <form action="." method="post" accept-charset="utf-8">
-
- <div id="form-row"><label for="id_openid_url">{% trans "OpenID URL:" %}</label>{{ form.openid_url }}</div>
- <p><input type="submit" value="{% trans "Change OpenID" %}"></p>
-
- </form>
- </div>
-{% endblock %}
-<!-- end changeopenid.html -->
diff --git a/forum/skins/default/templates/authopenid/changepw.html b/forum/skins/default/templates/authopenid/changepw.html
deleted file mode 100644
index 8b059544..00000000
--- a/forum/skins/default/templates/authopenid/changepw.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "base.html" %}
-<!-- changepw.html -->
-{% load i18n %}
-{% block head %}{% endblock %}
-{% block title %}{% spaceless %}{% trans "Change password" %}{% endspaceless %}{% endblock %}
-{% block content %}
-<div class="headNormal">{% trans "Account: change password" %}</div>
-<p class="message">{% blocktrans %}This is where you can change your password. Make sure you remember it!{% endblocktrans %}</p>
-<div class="aligned">
- <form action="." method="post" accept-charset="utf-8">
- <ul id="changepw-form" class="form-horizontal-rows">
- {{form.as_ul}}
- </ul>
- <div class="submit-row"><input type="submit" class="submit" value="{% trans "Change password" %}" /></div>
- </form>
- </div>
-{% endblock %}
-<!-- end changepw.html -->
diff --git a/forum/skins/default/templates/authopenid/complete.html b/forum/skins/default/templates/authopenid/complete.html
deleted file mode 100644
index 62970e38..00000000
--- a/forum/skins/default/templates/authopenid/complete.html
+++ /dev/null
@@ -1,130 +0,0 @@
-{% extends "base_content.html" %}
-<!-- complete.html -->
-{% comment %}
-views calling this template:
-* django_authopenid.views.register with login_type='openid'
-* django_authopenid.views.signin - with login_type='legacy'
-
-parameters:
-* provider
-* login_type openid|legacy
-* username (same as screen name or username in the models, and nickname in openid sreg)
-* form1 - OpenidRegisterForm
-* form2 - OpenidVerifyForm not clear what this form is supposed to do, not used for legacy
-* email_feeds_form forum.forms.SimpleEmailSubscribeForm
-* openid_username_exists
-{% endcomment %}
-{% load i18n %}
-{% block head %}{% endblock %}
-{% block title %}{% spaceless %}{% trans "Connect your OpenID with this site" %}{% endspaceless %}{% endblock %}
-{% block content %}
- <div id="main-bar" class="headNormal">
- {% trans "Connect your OpenID with your account on this site" %}
- </div>
- <div id="completetxt" >
- <div class="message">
- {% ifequal login_type 'openid' %}
- {% blocktrans %}register new {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% else %}
- {% ifequal login_type 'legacy' %}
- {% if external_login_name_is_taken %}
- {% blocktrans %}{{username}} already exists, choose another name for
- {{provider}}. Email is required too, see {{gravatar_faq_url}}
- {% endblocktrans %}
- {% else %}
- {% blocktrans %}register new external {{provider}} account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% endif %}
- {% else %}
- {% blocktrans %}register new Facebook connect account info, see {{gravatar_faq_url}}{% endblocktrans %}
- {% endifequal %}
- {% endifequal %}
- </div>
- <p style="display:none">{% trans "This account already exists, please use another." %}</p>
- </div>
-
- {% if form1.errors %}
- <ul class="errorlist">
- {% if form1.non_field_errors %}
- {% for error in form1.non_field_errors %}
- <li>{{error}}</li>
- {% endfor %}
- {% endif %}
- </ul>
- {% endif %}
- {% comment %}
- {% if form2.errors %}<!--form2 is dysfunctional so commented out -->
- <div class="errors">
- <span class="big">{% trans "Sorry, looks like we have some errors:" %}</span><br/>
- <ul class="error-list">
- {% if form2.username.errors %}
- <li><span class="error">{{ form2.username.errors|join:", " }}</span></li>
- {% endif %}
- {% if form2.password.errors %}
- <li><span class="error">{{ form2.password.errors|join:", " }}</span></li>
- {% endif %}
- </ul>
- </div>
- {% endif %}
- {% endcomment %}
-
- <div class="login">
- {% ifequal login_type 'openid' %}
- <form name="fregister" action="{% url user_register %}" method="POST">
- {% else %}
- {% ifequal login_type 'facebook' %}
- <form name="fregister" action="" method="POST">
- {% else %}
- <form name="fregister" action="{% url user_signin %}" method="POST">
- {% endifequal %}
- {% endifequal %}
- {{ form1.next }}
- <div class="form-row-vertical">
- <label for="id_username">{% trans "Screen name label" %}</label>
- {% if form1.username.errors %}
- <p class="error">{{ form1.username.errors|join:", " }}</p>
- {% endif %}
- {{ form1.username }}
- </div>
- <div class="form-row-vertical margin-bottom">
- <label for="id_email">{% trans "Email address label" %}</label>
- {% if form1.email.errors %}
- <p class="error">{{ form1.email.errors|join:", " }}</p>
- {% endif %}
- {{ form1.email }}
- </div>
- <p>{% trans "receive updates motivational blurb" %}</p>
- <div class='simple-subscribe-options'>
- {{email_feeds_form.subscribe}}
- {% if email_feeds_form.errors %}
- <p class="error">{% trans "please select one of the options above" %}</p>
- {% endif %}
- </div>
- <p class='space-above'>{% trans "Tag filter tool will be your right panel, once you log in." %}</p>
- <div class="submit-row"><input type="submit" class="submit" name="bnewaccount" value="{% trans "create account" %}"/></div>
- </form>
- </div>
- {% comment %}<!-- this form associates openID with an existing password-protected account, not yet functional -->
- {% if form2 %}
- <div class="login" style="display:none">
- <form name="fverify" action="{% url user_register %}" method="POST">
- {{ form2.next }}
- <fieldset style="padding:10px">
- <legend class="big">{% trans "Existing account" %}</legend>
- <div class="form-row"><label for="id_username">{% trans "user name" %}</label><br/>{{ form2.username }}</div>
- <div class="form-row"><label for="id_passwordl">{% trans "password" %}</label><br/>{{ form2.password }}</div>
- <p><span class='big strong'>(Optional) receive updates by email</span> - only sent when there are any.</p>
- <div class='simple-subscribe-options'>
- {{email_feeds_form.subscribe}}
- </div>
- <!--todo double check translation from chinese 确认 = "Register" -->
- <div class="submit-row">
- <input type="submit" class="submit" name="bverify" value="{% trans "Register" %}"/>
- <a href="{% url user_sendpw %}">{% trans "Forgot your password?" %}</a>
- </div>
- </fieldset>
- </form>
- </div>
- {% endif %}
- {% endcomment %}
-{% endblock %}
-<!-- end complete.html -->
diff --git a/forum/skins/default/templates/authopenid/confirm_email.txt b/forum/skins/default/templates/authopenid/confirm_email.txt
deleted file mode 100644
index 3a01f146..00000000
--- a/forum/skins/default/templates/authopenid/confirm_email.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-{% load i18n %}
-{% trans "Thank you for registering at our Q&A forum!" %}
-
-{% trans "Your account details are:" %}
-
-{% trans "Username:" %} {{ username }}
-{% trans "Password:" %} {{ password }}
-
-{% trans "Please sign in here:" %}
-{{signup_url}}
-
-{% blocktrans %}Sincerely,
-Forum Administrator{% endblocktrans %}
diff --git a/forum/skins/default/templates/authopenid/delete.html b/forum/skins/default/templates/authopenid/delete.html
deleted file mode 100644
index 0f9f1c60..00000000
--- a/forum/skins/default/templates/authopenid/delete.html
+++ /dev/null
@@ -1,39 +0,0 @@
-{% extends "base.html" %}
-<!-- delete.html -->
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Delete account" %}{% endspaceless %}{% endblock %}
-{% block content %}
-<div id="main-bar" class="">
- <h3>
- {% trans "Account: delete account" %}
- </h3>
-</div>
-
-<p class="settings-descr">{% blocktrans %}Note: After deleting your account, anyone will be able to register this username.{% endblocktrans %}</p>
-{% if form.errors %}
-<p class="errors">{% trans "Please correct errors below:" %}<br />
- {% if form.confirm.errors %}
- <span class="error">{% trans "Check confirm box, if you want delete your account." %}</span><br />
- {% endif %}
- {% if form.password.errors %}
- <span class="error">{% trans "Password:" %} {{ form.password.errors|join:", " }}</span>
- {% endif %}
-</p>
-{% endif %}
-{% if msg %}
-<p class="errors">{% trans "Please correct errors below:" %}<br />
- <span class="error">{{ msg }}</span>
- </p>
-{% endif %}
-<div class="aligned">
- <form action="." method="post" accept-charset="utf-8">
-
- <div id="form-row"> {{ form.confirm }} {% trans "I am sure I want to delete my account." %}</div>
- <div id="form-row"><label for="id_password">{% trans "Password/OpenID URL" %}</label>{{ form.password }} {% trans "(required for your security)" %}</div>
-
- <p><input type="submit" value="{% trans "Delete account permanently" %}"></p>
-
- </form>
- </div>
-{% endblock %}
-<!-- end delete.html -->
diff --git a/forum/skins/default/templates/authopenid/email_validation.txt b/forum/skins/default/templates/authopenid/email_validation.txt
deleted file mode 100644
index 5b166a9b..00000000
--- a/forum/skins/default/templates/authopenid/email_validation.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-{% load i18n %}
-{% trans "Greetings from the Q&A forum" %},
-
-{% trans "To make use of the Forum, please follow the link below:" %}
-
-{{validation_link}}
-
-{% trans "Following the link above will help us verify your email address." %}
-
-{% blocktrans %}If you beleive that this message was sent in mistake -
-no further action is needed. Just ingore this email, we apologize
-for any inconvenience{% endblocktrans %}
-
-{% blocktrans %}Sincerely,
-Forum Administrator{% endblocktrans %}
diff --git a/forum/skins/default/templates/authopenid/external_legacy_login_info.html b/forum/skins/default/templates/authopenid/external_legacy_login_info.html
deleted file mode 100644
index 3318499c..00000000
--- a/forum/skins/default/templates/authopenid/external_legacy_login_info.html
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "base_content.html" %}
-<!--customize this template-->
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Traditional login information" %}{% endspaceless %}{% endblock %}
-{% block content %}
-<div class="headNormal">
- {% trans "Traditional login information" %}
-</div>
-{% spaceless %}
-<div class="message">
-<!--add info about your external login site here-->
-{% blocktrans %}how to login with password through external login website or use {{feedback_url}}{% endblocktrans %}
-</div>
-{% endspaceless %}
-{% endblock %}
diff --git a/forum/skins/default/templates/authopenid/failure.html b/forum/skins/default/templates/authopenid/failure.html
deleted file mode 100644
index d075d6b0..00000000
--- a/forum/skins/default/templates/authopenid/failure.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/html4/strict.dtd">
-<!-- failure.html -->
-<html>
-<head>
- <title>OpenID failed</title>
-</head>
-<body>
-<h1>OpenID failed</h1>
-
-<p>{{ message|escape }}</p>
-
-</body>
-</html><!-- end failure.html -->
diff --git a/forum/skins/default/templates/authopenid/sendpw.html b/forum/skins/default/templates/authopenid/sendpw.html
deleted file mode 100644
index 6241c811..00000000
--- a/forum/skins/default/templates/authopenid/sendpw.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends "base.html" %}
-<!-- sendpw.html -->
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Send new password" %}{% endspaceless %}{% endblock %}
-{% block content %}
-<div class="headNormal">
- {% trans "Send new password" %}
-</div>
-<p class="message">
-{% trans "password recovery information" %}
-</p>
-{% if msg %}
- <p class="action-status"><span>{{msg}}</span><p>
-{% endif %}
-
-<div class="aligned">
- <form action="." method="post" accept-charset="utf-8">
- <ul id="emailpw-form" class="form-horizontal-rows">
- {{form.as_ul}}
- </ul>
- <p style="padding-top:10px"><input type="submit" class="submit" value="{% trans "Reset password" %}" />
- <a href="{% url user_signin %}"><span class="strong">{% trans "return to login" %}</span></a></p>
- </form>
-</div>
-{% endblock %}
-<!-- end sendpw.html -->
diff --git a/forum/skins/default/templates/authopenid/sendpw_email.txt b/forum/skins/default/templates/authopenid/sendpw_email.txt
deleted file mode 100644
index f044ca45..00000000
--- a/forum/skins/default/templates/authopenid/sendpw_email.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-{% load i18n %}
-{% blocktrans%}Someone has requested to reset your password on {{site_url}}.
-If it were not you, it is safe to ignore this email.{% endblocktrans %}
-
-{% blocktrans %}email explanation how to use new {{password}} for {{username}}
-with the {{key_link}}{% endblocktrans %}
-
-{% blocktrans %}Sincerely,
-Forum Administrator{% endblocktrans %}
diff --git a/forum/skins/default/templates/authopenid/settings.html b/forum/skins/default/templates/authopenid/settings.html
deleted file mode 100644
index 66ea5953..00000000
--- a/forum/skins/default/templates/authopenid/settings.html
+++ /dev/null
@@ -1,43 +0,0 @@
-{% extends "base_content.html" %}
-<!-- settings.html -->
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Account functions" %}{% endspaceless %}{% endblock %}
-{% block head %}
-<style type="text/css" media="screen">
- h4 {font-size:12pt;}
- dt, dd { padding:0 0 0.35em 0; }
- dt { float: left; width: 21ex; }
- dd { margin-left: 23ex; }
-
- #settings-options, #settings-intro { padding: 4em 1.5em;}
- #settings-options { min-height: 300px; border-left: 1px solid #333;}
-
- #settings-options h5 { font-weight: bold;}
-</style>
-{% endblock %}
-
-{% block content %}
-<div id="main-bar">
- <h3><strong>{{ request.user.username }} {% trans "Profile" %}</strong></h3>
-</div>
-<div id="settings-options">
- {% if msg %}
- <p class="error">{{ msg }}</p>
- {% endif %}
-
- <dl class="list-item">
- <dt>» <a href="{% url user_changepw %}">{% trans "Change password" %}</a></dt>
- <dd>{% trans "Give your account a new password." %}</dd>
- <dt>» <a href="{% url user_changeemail %}">{% trans "Change email " %}</a></dt>
- <dd>{% trans "Add or update the email address associated with your account." %}</dd>
- {% if is_openid %}
- <dt>» <a href="{% url user_changeopenid %}">{% trans "Change OpenID" %}</a></dt>
- <dd>{% trans "Change openid associated to your account" %}</dd>
- {% endif %}
-
- <dt>» <a href="{% url user_delete %}">{% trans "Delete account" %}</a></dt>
- <dd>{% trans "Erase your username and all your data from website" %}</dd>
- </dl>
-</div>
-{% endblock %}
-<!-- end settings.html -->
diff --git a/forum/skins/default/templates/authopenid/signin.html b/forum/skins/default/templates/authopenid/signin.html
deleted file mode 100755
index 4e060d0f..00000000
--- a/forum/skins/default/templates/authopenid/signin.html
+++ /dev/null
@@ -1,186 +0,0 @@
-{% extends "base.html" %}
-<!-- signin.html -->
-{% load i18n %}
-{% load extra_tags %}
-{% block title %}{% spaceless %}{% trans "User login" %}{% endspaceless %}{% endblock %}
-{% block forejs %}
- <script type='text/javascript' src='{% media "/media/js/jquery.validate.pack.js" %}'></script>
-
- <link rel="stylesheet" type="text/css" media="screen" href="{% media "/media/jquery-openid/openid.css" %}"/>
- <script type="text/javascript" src="{% media "/media/jquery-openid/jquery.openid.js" %}"></script>
- <script type="text/javascript"> $().ready( function() { $("form.openid:eq(0)").openid(); })</script>
- <!--<script type="text/javascript">
- $().ready(function(){
- openid.init('id_openid_url');
- setupFormValidation("#openid_form", {bsignin:{required: true}});
- });
- </script>-->
-{% endblock %}
-{% block content %}
-<div class="headNormal">
- {% trans "User login" %}
-</div>
- {% if msg %}
- <p class="warning">{{ msg }}</p>
- {% endif %}
- {% if answer %}
- <div class="message">
- {% blocktrans with answer.question.title as title and answer.summary as summary %}
- Your answer to {{title}} {{summary}} will be posted once you log in
- {% endblocktrans %}
- </div>
- {% endif %}
- {% if question %}
- <div class="message">
- {% blocktrans with question.title as title and question.summary as summary %}Your question
- {{title}} {{summary}} will be posted once you log in
- {% endblocktrans %}
- </div>
- {% endif %}
- <form id="openid_form" name="openid_form" class="openid" method="post" action="{% url user_signin %}">
- <div style="width:600px;float:left;margin-bottom:5px;">
- {% trans "Click to sign in through any of these services." %}
- </div>
- <ul class="providers">
- <li class="local" title="Local login">
- <div class="logo_box local_login_box">
- <img src="{% media "/media/jquery-openid/images/local-login.png" %}" alt="your icon here" />
- </div>
- <span></span>
- </li>
- <li class="direct" title="Google">
- <div class="logo_box google_box">
- <img src="{% media "/media/jquery-openid/images/google.gif" %}" alt="icon" /><span>https://www.google.com/accounts/o8/id</span>
- </div>
- </li>
- <li class="direct" title="Yahoo">
- <div class="logo_box yahoo_box">
- <img src="{% media "/media/jquery-openid/images/yahoo.gif" %}" alt="icon" /><span>http://yahoo.com/</span>
- </div>
- </li>
- <li class="username" title="AOL screen name">
- <div class="logo_box aol_box">
- <img src="{% media "/media/jquery-openid/images/aol.gif" %}" alt="icon" /><span>http://openid.aol.com/<strong>username</strong></span>
- </div>
- </li>
- </ul>
- <ul id="openid_small_providers" class="providers">
- <!--<li class="openid" title="OpenID">
- <div class="logo_box openid_box">
- <img src="/media/jquery-openid/images/openid.gif" alt="icon" />
- </div>
- <span><strong>http://{your-openid-url}</strong></span>
- </li>-->
- <li class="first_tiny_li facebook" title="Facebook Connect">
- {% if question %}
- <fb:login-button onlogin="window.location = '{% url fb_signin_new_question %}'"></fb:login-button>
- {% else %}
- {% if answer %}
- <fb:login-button onlogin="window.location = '{% url fb_signin_new_answer %}'"></fb:login-button>
- {% else %}
- <fb:login-button onlogin="window.location = '{% url fb_signin %}'"></fb:login-button>
- {% endif %}
- {% endif %}
- </li>
- <li class="openid" title="OpenID URL">
- <img src="{% media "/media/jquery-openid/images/openidico16.png" %}" alt="icon" />
- <span>http://{your-openid-url}</span>
- </li>
- <li class="username" title="MyOpenID user name">
- <img src="{% media "/media/jquery-openid/images/myopenid-2.png" %}" alt="icon" />
- <span>http://<strong>username</strong>.myopenid.com/</span>
- </li>
- <li class="username" title="Flickr user name">
- <img src="{% media "/media/jquery-openid/images/flickr.png" %}" alt="icon" />
- <span>http://flickr.com/<strong>username</strong>/</span>
- </li>
- <li class="username" title="Technorati user name">
- <img src="{% media "/media/jquery-openid/images/technorati-1.png" %}" alt="icon" />
- <span>http://technorati.com/people/technorati/<strong>username</strong>/</span>
- </li>
- <li class="username" title="Wordpress blog name">
- <img src="{% media "/media/jquery-openid/images/wordpress.png" %}" alt="icon" />
- <span>http://<strong>username</strong>.wordpress.com</span>
- </li>
- <li class="username" title="Blogger blog name">
- <img src="{% media "/media/jquery-openid/images/blogger-1.png" %}" alt="icon" />
- <span>http://<strong>username</strong>.blogspot.com/</span>
- </li>
- <li class="username" title="LiveJournal blog name">
- <img src="{% media "/media/jquery-openid/images/livejournal-1.png" %}" alt="icon" />
- <span>http://<strong>username</strong>.livejournal.com</span>
- </li>
- <li class="username" title="ClaimID user name">
- <img src="{% media "/media/jquery-openid/images/claimid-0.png" %}" alt="icon" />
- <span>http://claimid.com/<strong>username</strong></span>
- </li>
- <li class="username" title="Vidoop user name">
- <img src="{% media "/media/jquery-openid/images/vidoop.png" %}" alt="icon" />
- <span>http://<strong>username</strong>.myvidoop.com/</span>
- </li>
- <li class="username" title="Verisign user name">
- <img src="{% media "/media/jquery-openid/images/verisign-2.png" %}" alt="icon" />
- <span>http://<strong>username</strong>.pip.verisignlabs.com/</span>
- </li>
- </ul>
- {{ form2.next }}
- <fieldset>
- <p id="provider_name_slot">{% trans 'Enter your <span id="enter_your_what">Provider user name</span>' %}</p>
- <div><p><span></span>
- <input id="openid_username" type="text" name="openid_username" /><span></span>
- <input type="submit" value="Login" />
- </p></div>
- </fieldset>
- <fieldset>
- <p>{% trans 'Enter your <a class="openid_logo" href="http://openid.net">OpenID</a> web address' %}</p>
- <div><p><input id="openid_url" type="text" value="http://" name="openid_url" />
- <input id="bsignin" name="bsignin" type="submit" value="{% trans "Login" %}" /></p></div>
- </fieldset>
- <fieldset id='local_login_fs'>
- <p>{% trans 'Enter your login name and password' %}</p>
- {% if form1.errors %}
- {{form1.non_field_errors.as_ul}}
- {% endif %}
- <div><p class="login"><label for="id_username">{% trans "Login name" %}</label>
- {{form1.username}}</p>
- <p class="login"><label for="id_password">{% trans "Password" %}</label>
- {{form1.password}}</p>
- <p id="local_login_buttons">
- <input id="blogin" name="blogin" type="submit" value="{% trans "Login" %}" />
- <a href="{% url user_signup %}">{% trans "Create account" %}</a><br/>
- <a href="{% url user_sendpw %}">{% trans "Forgot your password?" %}</a>
- </p>
- </div>
- </fieldset>
- </form>
-{% endblock %}
-
-{% block sidebar %}
-<div class="boxC">
- <h3 class="subtitle">{% trans "Why use OpenID?" %}</h3>
- <ul class="list-item">
- <li>
- {% trans "with openid it is easier" %}
- </li>
- <li>
- {% trans "reuse openid" %}
- </li>
- <li>
- {% trans "openid is widely adopted" %}
- </li>
- <li>
- {% trans "openid is supported open standard" %}
- </li>
-
- </ul>
- <p class="info-box-follow-up-links">
- <a href="http://openid.net/what/" target="_blank">{% trans "Find out more" %} »</a><br/>
- <a href="http://openid.net/get/" target="_blank">{% trans "Get OpenID" %} »</a>
- </p>
-</div>
-<script type="text/javascript" src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php"></script>
-<script type="text/javascript"> FB.init("{{ fb_api_key }}","{% url xd_receiver %}");</script>
-{% endblock%}
-
- <script type="text/javascript"> $( function() { $("form.openid:eq(0)").openid(); })</script>
-<!-- end signin.html -->
diff --git a/forum/skins/default/templates/authopenid/signup.html b/forum/skins/default/templates/authopenid/signup.html
deleted file mode 100644
index fdb236c2..00000000
--- a/forum/skins/default/templates/authopenid/signup.html
+++ /dev/null
@@ -1,32 +0,0 @@
-{% extends "base_content.html" %}
-<!--signup.html-->
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Signup" %}{% endspaceless %}{% endblock %}
-
-{% block content %}
-<div class="headNormal">
- {% trans "Create login name and password" %}
-</div>
-<p class="message">{% trans "Traditional signup info" %}</p>
-<form action="{% url user_signup %}" method="post" accept-charset="utf-8">
- <ul class="form-horizontal-rows">
- <li><label for="usename_id">{{form.username.label}}</label>{{form.username}}{{form.username.errors}}</li>
- <li><label for="email_id">{{form.email.label}}</label>{{form.email}}{{form.email.errors}}</li>
- <li><label for="password1_id">{{form.password1.label}}</label>{{form.password1}}{{form.password1.errors}}</li>
- <li><label for="password2_id">{{form.password2.label}}</label>{{form.password2}}{{form.password2.errors}}</li>
- </ul>
- <p class="margin-top">{% trans "receive updates motivational blurb" %}</p>
- <div class='simple-subscribe-options'>
- {{email_feeds_form.subscribe}}
- {% if email_feeds_form.errors %}
- <p class="error">{% trans "please select one of the options above" %}</p>
- {% endif %}
- </div>
- <p class="signup_p">{% trans "Please read and type in the two words below to help us prevent automated account creation." %}</p>
- {{form.recaptcha}}
- <div class="submit-row"><input type="submit" class="submit" value="{% trans "Create Account" %}" />
- <strong>{% trans "or" %}
- <a href="{% url user_signin %}">{% trans "return to OpenID login" %}</a></strong></div>
-</form>
-{% endblock %}
-<!--end signup.html-->
diff --git a/forum/skins/default/templates/authopenid/yadis.xrdf b/forum/skins/default/templates/authopenid/yadis.xrdf
deleted file mode 100644
index a9ed44fe..00000000
--- a/forum/skins/default/templates/authopenid/yadis.xrdf
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<xrds:XRDS
- xmlns:xrds='xri://$xrds'
- xmlns:openid='http://openid.net/xmlns/1.0'
- xmlns='xri://$xrd*($v*2.0)'>
- <XRD>
- <Service>
- <Type>http://specs.openid.net/auth/2.0/return_to</Type>
- {% for uri in return_to %}
- <URI>{{ uri }}</URI>
- {% endfor %}
- </Service>
- </XRD>
-</xrds:XRDS> \ No newline at end of file
diff --git a/forum/skins/default/templates/base.html b/forum/skins/default/templates/base.html
index 3a1848ef..884551e3 100755
--- a/forum/skins/default/templates/base.html
+++ b/forum/skins/default/templates/base.html
@@ -3,7 +3,7 @@
{% load extra_filters %}
{% load extra_tags %}
{% load i18n %}
-<html xmlns="http://www.w3.org/1999/xhtml"{% if fb_api_key %} xmlns:fb="http://www.facebook.com/2008/fbml"{% endif %}>
+<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{% block title %}{% endblock %} - {{ settings.APP_TITLE }}</title>
{% spaceless %}
diff --git a/forum/skins/default/templates/changepw.html b/forum/skins/default/templates/changepw.html
deleted file mode 100755
index 7b4cf801..00000000
--- a/forum/skins/default/templates/changepw.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "base.html" %}
-<!-- changepw.html -->
-{% load i18n %}
-{% block head %}{% endblock %}
-{% block title %}{% spaceless %}{% trans "Change password" %}{% endspaceless %}{% endblock %}
-{% block content %}
-<div class="headNormal">{% trans "Account: change password" %}</div>
-<p class="message">{% blocktrans %}This is where you can change your password. Make sure you remember it!{% endblocktrans %}</p>
-<div class="aligned">
- <form action="" method="post" accept-charset="utf-8">
- <ul id="changepw-form" class="form-horizontal-rows">
- {{form.as_ul}}
- </ul>
- <div class="submit-row"><input type="submit" class="submit" value="{% trans "Change password" %}" /></div>
- </form>
- </div>
-{% endblock %}
-<!-- end changepw.html -->
diff --git a/forum/skins/default/templates/email_base.html b/forum/skins/default/templates/email_base.html
new file mode 100755
index 00000000..b74741e3
--- /dev/null
+++ b/forum/skins/default/templates/email_base.html
@@ -0,0 +1,26 @@
+{% load extra_filters %}
+{% load extra_tags %}
+{% load i18n %}
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <link href="{% fullmedia "/media/style/style.css" %}" rel="stylesheet" type="text/css" />
+ </head>
+ <body>
+ <a href="{% fullurl index %}">
+ <img src="{% fullmedia "/media/images/logo.png" %}" title="{% trans "home" %}" alt="{{settings.APP_TITLE}} logo"/>
+ </a>
+ <br />
+ <p>{{ settings.APP_TITLE }}</p>
+ <br /><br />
+ <div id="wrapper">
+ <div id="room">
+ <div id="CALeft">
+ {% block content%}
+ {% endblock%}
+ </div>
+ </div>
+ <div class="spacer3"></div>
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/forum/skins/default/templates/footer.html b/forum/skins/default/templates/footer.html
index 2f95921d..66ea2bc5 100755
--- a/forum/skins/default/templates/footer.html
+++ b/forum/skins/default/templates/footer.html
@@ -21,7 +21,7 @@
{% endspaceless %}
</div>
<p>
- <a href="http://github.com/cnprog/CNPROG/network" target="_blank">
+ <a href="http://osqa.net" target="_blank">
powered by OSQA
</a>
</p>
diff --git a/forum/skins/default/templates/header.html b/forum/skins/default/templates/header.html
index cb12bb97..e65ac73a 100755
--- a/forum/skins/default/templates/header.html
+++ b/forum/skins/default/templates/header.html
@@ -5,7 +5,7 @@
<div id="navBar">
<div id="top">
{% if request.user.is_authenticated %}
- <a href="{% url users %}{{ request.user.id }}/{{ request.user.username }}/">{{ request.user.username }}</a> {% get_score_badge request.user %}
+ <a href="{% url user_profile id=request.user.id,slug=request.user.username|slugify %}">{{ request.user.username }}</a> {% get_score_badge request.user %}
<a href="{% url logout %}">{% trans "logout" %}</a>
{% else %}
<a href="{% url auth_signin %}">{% trans "login" %}</a>
diff --git a/forum/skins/default/templates/index.html b/forum/skins/default/templates/index.html
index 5bbb192b..61a5e6b3 100755
--- a/forum/skins/default/templates/index.html
+++ b/forum/skins/default/templates/index.html
@@ -5,6 +5,7 @@
{% load humanize %}
{% load extra_filters %}
{% load smart_if %}
+{% load cache %}
{% block title %}{% spaceless %}{% trans "Home" %}{% endspaceless %}{% endblock %}
{% block meta %}<meta name="keywords" content="{{ settings.APP_KEYWORDS }}" />
<meta name="description" content="{{ settings.APP_DESCRIPTION }}" />{% endblock %}
@@ -31,41 +32,7 @@
</div>
</div>
<!-- ???? -->
-<div id="listA">
- {% for question in questions.object_list %}
- <div class="short-summary">
- <div class="counts">
- <div class="votes">
- <div class="item-count">{{question.score|intcomma}}</div>
- <div>{% trans "votes" %}</div>
- </div >
- <div {% if question.answer_accepted %}title="{% trans "this answer has been accepted to be correct" %}"{% endif %} class="status {% if question.answer_accepted %}answered-accepted{% endif %} {% ifequal question.answer_count 0 %}unanswered{% endifequal %}{% ifnotequal question.answer_count 0 %}answered{% endifnotequal %}">
- <div class="item-count">{{question.answer_count|intcomma}}</div>
- <div>{% trans "answers" %}</div>
- </div>
- <div class="views">
- <div class="item-count">{{question.view_count|cnprog_intword|safe}}</div>
- <div>{% trans "views" %}</div>
- </div>
- </div>
-
- <h2><a title="{{question.summary}}" href="{% url question id=question.id %}{{question.title|slugify}}">{{question.title}}</a></h2>
-
- <div class="userinfo">
- <span class="relativetime" title="{{question.last_activity_at}}">{% diff_date question.last_activity_at %}</span>
- {% if question.last_activity_by %}
- <a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a> {% get_score_badge question.last_activity_by %}
- {% endif %}
- </div>
-
- <div class="tags">
- {% for tag in question.tagname_list %}
- <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %} '{{ tag }}' {% trans "using tags" %}" rel="tag">{{ tag }}</a>
- {% endfor %}
- </div>
- </div>
- {% endfor %}
-</div>
+{% include "question_list.html" %}
{% endblock %}
{% block sidebar %}
@@ -85,10 +52,12 @@
<h3>{% trans "Recent tags" %}</h3>
<div class="body">
<div class="tags">
+ {% cache 60 recent_tags %}
{% for tag in tags %}
<a rel="tag"
title="{% blocktrans with tag.name as tagname %}see questions tagged '{{tagname}}'{% endblocktrans %}" href="{% url tag_questions tag.name|urlencode %}">{{ tag.name }}</a>
{% endfor %}
+ {% endcache %}
</div>
<div class="more"><a href="{% url tags %}">{% trans "popular tags" %} </a> </div>
</div>
@@ -98,20 +67,22 @@
<h3>{% trans "Recent awards" %}</h3>
<div class="body">
<ul class="badge-list">
+ {% cache 60 recent_awards %}
{% for award in awards %}
<li>
<a href="{% url badges %}{{award.badge_id}}/{{award.badge_name}}" title="{{ award.badge_description }}" class="medal">
- <span class="badge{{ award.badge_type }}">&#9679;</span>&nbsp;{{ award.badge_name }}</a> {% trans "given to" %}
- <a href="{% url users %}{{award.user_id}}/{{award.user_name}}">{{ award.user_name }}</a>
+ <span class="badge{{ award.badge_type }}">&#9679;</span>&nbsp;{{ award.badge_name }}</a>
+ <a href="/users/{{ award.user_id }}/{{ award.user_name|slugify }}/">{{ award.user_name }}</a>
</li>
{% endfor %}
+ {% endcache %}
</ul>
<div class="more"><a href="{% url badges %}">{% trans "all awards" %} </a> </div>
</div>
</div>
{% endif %}
<div id="feeds">
-<a href="{% media "/feeds/rss" %}" title="{% trans "subscribe to last 30 questions by RSS" %}">{% trans "subscribe to the questions feed" %}</a>
+<a href="{% url latest_questions_feed %}" title="{% trans "subscribe to last 30 questions by RSS" %}">{% trans "subscribe to the questions feed" %}</a>
</div>
{% endblock %}
{% block tail %}
diff --git a/forum/skins/default/templates/question.html b/forum/skins/default/templates/question.html
index 23c24113..3df778dc 100755
--- a/forum/skins/default/templates/question.html
+++ b/forum/skins/default/templates/question.html
@@ -5,6 +5,7 @@
{% load smart_if %}
{% load humanize %}
{% load i18n %}
+{% load cache %}
{% block title %}{% spaceless %}{{ question.get_question_title }}{% endspaceless %}{% endblock %}
{% block forejs %}
<meta name="description" content="{{question.summary}}" />
@@ -164,8 +165,8 @@
{% endjoinitems %}
</div>
<div class="post-update-info-container">
- {% post_contributor_info question "original_author" %}
- {% post_contributor_info question "last_updater" %}
+ {% post_contributor_info question "original_author" %}
+ {% post_contributor_info question "last_updater" %}
</div>
<div class="comments-container" id="comments-container-question-{{question.id}}">
{% for comment in question.get_comments|slice:":5" %}
@@ -313,8 +314,8 @@
{% endjoinitems %}
</div>
<div class="post-update-info-container">
- {% post_contributor_info answer "original_author" %}
- {% post_contributor_info answer "last_updater" %}
+ {% post_contributor_info answer "original_author" %}
+ {% post_contributor_info answer "last_updater" %}
</div>
<div class="comments-container" id="comments-container-answer-{{answer.id}}">
{% for comment in answer.get_comments|slice:":5" %}
@@ -468,6 +469,7 @@
{% endblock %}
{% block sidebar %}
+{% cache 600 questions_tags question.id %}
<div class="boxC">
<p>
{% trans "Question tags" %}:
@@ -489,17 +491,21 @@
{% trans "last updated" %}: <strong title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</strong>
</p>
</div>
-
+{% endcache %}
+{% cache 1800 related_questions question.id %}
<div class="boxC">
<h3 class="subtitle">{% trans "Related questions" %}</h3>
<div class="questions-related">
- {% for question in similar_questions %}
+
+ {% for question in similar_questions.data %}
<p>
<a href="{{ question.get_absolute_url }}">{{ question.get_question_title }}</a>
</p>
{% endfor %}
+
</div>
</div>
+{% endcache %}
{% endblock %}
diff --git a/forum/skins/default/templates/question_list.html b/forum/skins/default/templates/question_list.html
new file mode 100755
index 00000000..cf69d133
--- /dev/null
+++ b/forum/skins/default/templates/question_list.html
@@ -0,0 +1,42 @@
+{% load cache %}
+{% load i18n %}
+{% load humanize %}
+{% load extra_filters %}
+{% load extra_tags %}
+
+<div id="listA">
+ {% for question in questions.object_list %}
+ {% cache 60 question_in_list question.id %}
+ <div class="short-summary">
+ <div class="counts">
+ <div class="votes">
+ <div class="item-count">{{question.score|intcomma}}</div>
+ <div>{% trans "votes" %}</div>
+ </div >
+ <div {% if question.answer_accepted %}title="{% trans "this answer has been accepted to be correct" %}"{% endif %} class="status {% if question.answer_accepted %}answered-accepted{% endif %} {% ifequal question.answer_count 0 %}unanswered{% endifequal %}{% ifnotequal question.answer_count 0 %}answered{% endifnotequal %}">
+ <div class="item-count">{{question.answer_count|intcomma}}</div>
+ <div>{% trans "answers" %}</div>
+ </div>
+ <div class="views">
+ <div class="item-count">{{question.view_count|cnprog_intword|safe}}</div>
+ <div>{% trans "views" %}</div>
+ </div>
+ </div>
+
+ <h2><a title="{{question.summary}}" href="{% url question id=question.id %}{{question.title|slugify}}">{{question.title}}</a></h2>
+ <div class="userinfo">
+ <span class="relativetime" title="{{question.last_activity_at}}">{% diff_date question.last_activity_at %}</span>
+ {% if question.last_activity_by %}
+ <a href="/users/{{ question.last_activity_by.id }}/{{ question.last_activity_by.username|slugify }}/">{{ question.last_activity_by }}</a> {% get_score_badge question.last_activity_by %}
+ {% endif %}
+ </div>
+
+ <div class="tags">
+ {% for tag in question.tagname_list %}
+ <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %} '{{ tag }}' {% trans "using tags" %}" rel="tag">{{ tag }}</a>
+ {% endfor %}
+ </div>
+ </div>
+ {% endcache %}
+ {% endfor %}
+</div> \ No newline at end of file
diff --git a/forum/skins/default/templates/questions.html b/forum/skins/default/templates/questions.html
index c2d74eca..de6076ac 100755
--- a/forum/skins/default/templates/questions.html
+++ b/forum/skins/default/templates/questions.html
@@ -50,106 +50,7 @@
<a id="mostvoted" href="{% if is_search %}{{ search_uri }}&{% else %}?{% endif %}sort=mostvoted" class="off" title="{% trans "most voted questions" %}">{% trans "most voted" %}</a>
</div>
</div>
-<div id="listA">
- {% for question in questions.object_list %}
- <div class="qstA"
- {% if request.user.is_authenticated %}
- {% if question.interesting_score > 0 %}
- style="background:#ffff99;"
- {% else %}
- {% if not request.user.hide_ignored_questions %}
- {% if question.ignored_score > 0 %}
- style="background:#f3f3f3;"
- {% endif %}
- {% endif %}
- {% endif %}
- {% endif %}
- >
- <h2>
- <a href="{{ question.get_absolute_url }}">{{ question.get_question_title }}</a>
- </h2>
- <div class="stat">
- <table>
- <tr>
- <td><span class="num">{{ question.answer_count|intcomma }}</span> </td>
- <td><span class="num">{{ question.score|intcomma }}</span> </td>
- <td><span class="num">{{ question.view_count|cnprog_intword|safe }}</span> </td>
- </tr>
- <tr>
- <td><span class="unit">{% trans "answers" %}</span></td>
- <td><span class="unit">{% trans "votes" %}</span></td>
- <td><span class="unit">{% trans "views" %}</span></td>
- </tr>
- </table>
- </div>
-
- <div class="summary">
- {{ question.summary }}...
- </div>
-
- {% ifequal tab_id 'active'%}
- {% if question.wiki and settings.WIKI_ON %}
- <span class="from wiki">{% trans "community wiki" %}</span>
- <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span>
- {% else %}
- <div class="from">
- {% comment %}{% gravatar question.last_activity_by 24 %}{% endcomment %}
- <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span>
- <span class="score">{% get_score_badge question.last_activity_by %} </span>
- <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span>
- </div>
- {% endif %}
- {% else %}
- {% if question.wiki and settings.WIKI_ON %}
- <span class="from wiki">{% trans "community wiki" %}</span>
- <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span>
- {% else %}
- <div class="from">
- {% comment %}{% gravatar question.author 24 %}{% endcomment %}
- {% if question.last_activity_at != question.added_at %}
- {% if question.author.id != question.last_activity_by.id %}
- {% trans "Posted:" %}
- <span class="author"><a href="{{ question.author.get_profile_url }}">{{ question.author }}</a></span>
- <span class="score">{% get_score_badge question.author %} </span>
- / {% trans "Updated:" %}
- <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span>
- <span class="score">{% get_score_badge question.last_activity_by %} </span>
- <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span>
- {% else %}
- {% trans "Updated:" %}
- <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span>
- <span class="score">{% get_score_badge question.last_activity_by %} </span>
- <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span>
- {% endif %}
- {% else %}
- {% trans "Posted:" %}
- <span class="author"><a href="{{ question.author.get_profile_url }}">{{ question.author }}</a></span>
- <span class="score">{% get_score_badge question.author %} </span>
- <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span>
- {% endif %}
- </div>
- {% endif %}
- {% endifequal %}
-
- <div class="tags">
- {% for tag in question.tagname_list %}
- <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a>
- {% endfor %}
- </div>
- </div>
- {% endfor %}
- {% if searchtitle %}
- {% if questions_count == 0 %}
- <p class="evenMore" style="padding-top:30px;text-align:center;">
- {% trans "Did not find anything?" %}
- {% else %}
- <p class="evenMore" style="padding-left:9px">
- {% trans "Did not find what you were looking for?" %}
- {% endif %}
- <a href="{% url ask %}">{% trans "Please, post your question!" %}</a>
- </p>
- {% endif %}
-</div>
+{% include "question_list.html" %}
{% endblock %}
{% block tail %}
diff --git a/forum/skins/default/templates/tag_selector.html b/forum/skins/default/templates/tag_selector.html
index 7686d717..5e3e2ad8 100755
--- a/forum/skins/default/templates/tag_selector.html
+++ b/forum/skins/default/templates/tag_selector.html
@@ -37,6 +37,6 @@
<input id="ignoredTagAdd" type="submit" value="{% trans "Add" %}"/>
<p id="hideIgnoredTagsControl">
<input id="hideIgnoredTagsCb" type="checkbox" {% if request.user.hide_ignored_questions %}checked="checked"{% endif %} />
- <label id="hideIgnoredTagsLabel" for="hideIgnoredTagsCb">{% trans "keep ingored questions hidden" %}</label>
+ <label id="hideIgnoredTagsLabel" for="hideIgnoredTagsCb">{% trans "keep ignored questions hidden" %}</label>
<p>
</div>
diff --git a/forum/skins/default/templates/user_info.html b/forum/skins/default/templates/user_info.html
index 0ce4e77a..b0fd246a 100755
--- a/forum/skins/default/templates/user_info.html
+++ b/forum/skins/default/templates/user_info.html
@@ -46,12 +46,8 @@
{% endif %}
{% separator %}
{% ifequal request.user view_user %}
- <a href="{% url auth_signin %}">add authentication method</a>
+ <a href="{% url user_authsettings %}">authentication settings</a>
{% endifequal %}
- {% separator %}
- {% if request.user.has_usable_password %}
- <a href="{% url user_changepw %}">change password</a>
- {% endif %}
{% endjoinitems %}
</td>
</tr>
diff --git a/forum/skins/default/templates/user_stats.html b/forum/skins/default/templates/user_stats.html
index a3f88131..5ad1d71b 100755
--- a/forum/skins/default/templates/user_stats.html
+++ b/forum/skins/default/templates/user_stats.html
@@ -124,7 +124,7 @@
<tr>
<td width="180" style="line-height:35px">
{% for award in awards %}
- <a href="{% url badges %}{{award.id}}/{{award.name}}" title="{{ award.description }}" class="medal"><span class="badge{{ award.type }}">&#9679;</span>&nbsp;{{ award.name }}</a><span class="tag-number"> &#x2715; {{ award.count|intcomma }}</span><br/>
+ <a href="{% url badges %}{{award.id}}/{{award.name}}" title="{{ award.description }}" class="medal"><span class="badge{{ award.type }}">&#9679;</span>&nbsp;{{ award.name }}</a><span class="tag-number"> &#215; {{ award.count|intcomma }}</span><br/>
{% if forloop.counter|divisibleby:"6" %}
</td>
<td width="180" style="line-height:35px">
diff --git a/forum/skins/default/templates/user_tabs.html b/forum/skins/default/templates/user_tabs.html
index 908e8430..136a1eaa 100755
--- a/forum/skins/default/templates/user_tabs.html
+++ b/forum/skins/default/templates/user_tabs.html
@@ -4,28 +4,28 @@
<div class="tabBar">
<div class="tabsA">
<a id="stats" {% ifequal tab_name "stats" %}class="on"{% endifequal %}
- title="{% trans "User profile" %}" href="{% url users %}{{view_user.id}}/{{view_user.username}}?sort=stats">{% trans "overview" %}</a>
+ title="{% trans "User profile" %}" href="/users/{{ view_user.id }}/{{ view_user.username|slugify }}/?sort=stats">{% trans "overview" %}</a>
<a id="recent" {% ifequal tab_name "recent" %}class="on"{% endifequal %}
- title="{% trans "recent activity" %}" href="{% url users %}{{view_user.id}}/{{view_user.username}}?sort=recent">{% trans "recent activity" %}</a>
+ title="{% trans "recent activity" %}" href="/users/{{ view_user.id }}/{{ view_user.username|slugify }}?sort=recent">{% trans "recent activity" %}</a>
{% if request.user|is_user_self:view_user %}
<a id="responses" {% ifequal tab_name "responses" %}class="on"{% endifequal %}
title="{% trans "comments and answers to others questions" %}"
- href="{% url users %}{{view_user.id}}/{{view_user.username}}?sort=responses">{% trans "responses" %}</a>
+ href="/users/{{ view_user.id }}/{{ view_user.username|slugify }}?sort=responses">{% trans "responses" %}</a>
{% endif %}
<a id="reputation" {% ifequal tab_name "reputation" %}class="on"{% endifequal %}
title="{% trans "graph of user reputation" %}"
- href="{% url users %}{{view_user.id}}/{{view_user.username}}?sort=reputation">{% trans "reputation history" %}</a>
+ href="/users/{{ view_user.id }}/{{ view_user.username|slugify }}?sort=reputation">{% trans "reputation history" %}</a>
{% if request.user|can_view_user_votes:view_user %}
<a id="votes" {% ifequal tab_name "votes" %}class="on"{% endifequal %}
- title="{% trans "user vote record" %}" href="{% url users %}{{view_user.id}}/{{view_user.username}}?sort=votes">{% trans "casted votes" %}</a>
+ title="{% trans "user vote record" %}" href="/users/{{ view_user.id }}/{{ view_user.username|slugify }}?sort=votes">{% trans "casted votes" %}</a>
{% endif %}
<a id="favorites" {% ifequal tab_name "favorites" %}class="on"{% endifequal %}
title="{% trans "questions that user selected as his/her favorite" %}"
- href="{% url users %}{{view_user.id}}/{{view_user.username}}?sort=favorites">{% trans "favorites" %}</a>
+ href="/users/{{ view_user.id }}/{{ view_user.username|slugify }}?sort=favorites">{% trans "favorites" %}</a>
{% if request.user|can_view_user_preferences:view_user %}
<a id="email_subscriptions" {% ifequal tab_name "email_subscriptions" %}class="on"{% endifequal %}
title="{% trans "email subscription settings" %}"
- href="{% url users %}{{view_user.id}}/{{view_user.username}}?sort=email_subscriptions">{% trans "email subscriptions" %}</a>
+ href="/users/{{ view_user.id }}/{{ view_user.username|slugify }}?sort=email_subscriptions">{% trans "email subscriptions" %}</a>
{% endif %}
</div>
</div>
diff --git a/forum/skins/default/templates/users.html b/forum/skins/default/templates/users.html
index 3a59b0c0..f24b60a1 100755
--- a/forum/skins/default/templates/users.html
+++ b/forum/skins/default/templates/users.html
@@ -47,8 +47,8 @@
<div class="user">
<ul>
- <li class="thumb"><a href="{{ user.get_profile_url }}">{% gravatar user 32 %}</a></li>
- <li><a href="{{ user.get_profile_url }}">{{user.username}}</a></li>
+ <li class="thumb"><a href="/users/{{ user.id }}/{{ user.username|slugify }}/">{% gravatar user 32 %}</a></li>
+ <li><a href="/users/{{ user.id }}/{{ user.username|slugify }}/">{{user.username}}</a></li>
<li>{% get_score_badge user %}</li>
</ul>
</div>
diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py
index 26c52b8d..6dab868b 100755
--- a/forum/templatetags/extra_tags.py
+++ b/forum/templatetags/extra_tags.py
@@ -13,6 +13,7 @@ from forum.models import Question, Answer, QuestionRevision, AnswerRevision
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
from django.conf import settings
+from django.template.defaulttags import url as default_url
from forum import skins
register = template.Library()
@@ -355,3 +356,27 @@ def blockmedia(parser,token):
if next.contents == 'endblockmedia':
break
return BlockMediaUrlNode(nodelist)
+
+class FullUrlNode(template.Node):
+ def __init__(self, default_node):
+ self.default_node = default_node
+
+ def render(self, context):
+ domain = settings.APP_URL
+ #protocol = getattr(settings, "PROTOCOL", "http")
+ path = self.default_node.render(context)
+ return "%s%s" % (domain, path)
+
+@register.tag(name='fullurl')
+def fullurl(parser, token):
+ default_node = default_url(parser, token)
+ return FullUrlNode(default_node)
+
+@register.simple_tag
+def fullmedia(url):
+ domain = settings.APP_URL
+ #protocol = getattr(settings, "PROTOCOL", "http")
+ path = media(url)
+ return "%s%s" % (domain, path)
+
+
diff --git a/forum/urls.py b/forum/urls.py
index f81bad69..3e99af41 100755
--- a/forum/urls.py
+++ b/forum/urls.py
@@ -76,7 +76,7 @@ urlpatterns = patterns('',
url(r'^%s$' % _('users/'),app.users.users, name='users'),
url(r'^%s(?P<id>\d+)/$' % _('moderate-user/'), app.users.moderate_user, name='moderate_user'),
url(r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')), app.users.edit_user, name='edit_user'),
- url(r'^%s(?P<id>\d+)//*' % _('users/'), app.users.user, name='user'),
+ url(r'^%s(?P<id>\d+)/(?P<slug>.+)/$' % _('users/'), app.users.user, name='user_profile'),
url(r'^%s$' % _('badges/'),app.meta.badges, name='badges'),
url(r'^%s(?P<id>\d+)//*' % _('badges/'), app.meta.badge, name='badge'),
url(r'^%s%s$' % (_('messages/'), _('markread/')),app.commands.read_message, name='read_message'),
@@ -96,11 +96,18 @@ urlpatterns = patterns('',
url(r'^%s(?P<provider>\w+)/%s$' % (_('account/'), _('signin/')), app.auth.prepare_provider_signin, name='auth_provider_signin'),
url(r'^%s(?P<provider>\w+)/%s$' % (_('account/'), _('done/')), app.auth.process_provider_signin, name='auth_provider_done'),
url(r'^%s%s$' % (_('account/'), _('register/')), app.auth.external_register, name='auth_external_register'),
+ url(r'^%s%s(?P<user>\d+)/(?P<code>.+)/$' % (_('account/'), _('validate/')), app.auth.validate_email, name="auth_validate_email"),
+ url(r'^%s%s$' % (_('account/'), _('tempsignin/')), app.auth.request_temp_login, name="auth_request_tempsignin"),
+ url(r'^%s%s(?P<user>\d+)/(?P<code>.+)/$' % (_('account/'), _('tempsignin/')), app.auth.temp_signin, name="auth_tempsignin"),
+ url(r'^%s%s$' % (_('account/'), _('authsettings/')), app.auth.auth_settings, name='user_authsettings'),
+ url(r'^%s%s(?P<id>\d+)/%s$' % (_('account/'), _('providers/'), _('remove/')), app.auth.remove_external_provider, name='user_remove_external_provider'),
+ url(r'^%s%s%s$' % (_('account/'), _('providers/'), _('add/')), app.auth.signin_page, name='user_add_external_provider'),
- url(r'^%s%s$' % (_('account/'), _('password/')), app.users.changepw, name='user_changepw'),
#url(r'^%s%s%s$' % (_('accounts/'), _('password/'), _('confirm/')), app.user.confirmchangepw, name='user_confirmchangepw'),
- url(r'^%s$' % _('account/'), app.users.account_settings, name='user_account_settings'),
- #url(r'^%s$' % _('delete/'), app.users.delete, name='user_delete'),
+ #url(r'^%s$' % _('account/'), app.users.account_settings, name='user_account_settings'),
+ #url(r'^%s$' % _('delete/'), app.users.delete, name='user_delete'),
+
+ url(r'^feeds/rss/$', RssLastestQuestionsFeed, name="latest_questions_feed"),
)
from forum.modules import get_modules_script
diff --git a/forum/user_messages/context_processors.py b/forum/user_messages/context_processors.py
index 2bf26269..5f7b857c 100755
--- a/forum/user_messages/context_processors.py
+++ b/forum/user_messages/context_processors.py
@@ -17,6 +17,9 @@ def user_messages (request):
#if request.user.is_authenticated():
#else:
# messages = LazyMessages(request)
+ #import inspect
+ #print inspect.stack()[1]
+ #print messages
return { 'user_messages': messages }
class LazyMessages (StrAndUnicode):
diff --git a/forum/utils/email.py b/forum/utils/email.py
new file mode 100755
index 00000000..dc712572
--- /dev/null
+++ b/forum/utils/email.py
@@ -0,0 +1,21 @@
+from django.core.mail import send_mail, EmailMultiAlternatives
+from django.conf import settings
+from django.template import loader, Context
+from django.utils.html import strip_tags
+from threading import Thread
+
+def send_email(subject, recipients, template, context={}, sender=settings.DEFAULT_FROM_EMAIL, txt_template=None):
+ context['settings'] = settings
+ html_body = loader.get_template(template).render(Context(context))
+
+ if txt_template is None:
+ txt_body = strip_tags(html_body)
+ else:
+ txt_body = loader.get_template(txt_template).render(Context(context))
+
+ msg = EmailMultiAlternatives(subject, txt_body, sender, recipients)
+ msg.attach_alternative(html_body, "text/html")
+
+ thread = Thread(target=EmailMultiAlternatives.send, args=[msg])
+ thread.setDaemon(True)
+ thread.start()
diff --git a/forum/utils/forms.py b/forum/utils/forms.py
index c54056ca..c8305c7c 100755
--- a/forum/utils/forms.py
+++ b/forum/utils/forms.py
@@ -133,6 +133,10 @@ class SetPasswordForm(forms.Form):
error_messages={'required':_('please, retype your password'),
'nomatch':_('sorry, entered passwords did not match, please try again')},
)
+
+ def __init__(self, data=None, user=None, *args, **kwargs):
+ super(SetPasswordForm, self).__init__(data, *args, **kwargs)
+
def clean_password2(self):
"""
Validates that the two password inputs match.
diff --git a/forum/utils/lists.py b/forum/utils/lists.py
index bbcfae98..f69c8f20 100755
--- a/forum/utils/lists.py
+++ b/forum/utils/lists.py
@@ -1,5 +1,9 @@
"""Utilities for working with lists and sequences."""
+class LazyList(list):
+ def __init__(self, get_data):
+ self.data = get_data
+
def flatten(x):
"""
Returns a single, flat list which contains all elements retrieved
diff --git a/forum/utils/time.py b/forum/utils/time.py
new file mode 100755
index 00000000..39e01d0f
--- /dev/null
+++ b/forum/utils/time.py
@@ -0,0 +1,4 @@
+import datetime
+
+def one_day_from_now():
+ return datetime.datetime.now() + datetime.timedelta(days=1)
diff --git a/forum/views/auth.py b/forum/views/auth.py
index cf27f1bb..72b0af29 100755..100644
--- a/forum/views/auth.py
+++ b/forum/views/auth.py
@@ -1,17 +1,21 @@
-from django.shortcuts import render_to_response
+from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
-from django.http import HttpResponseRedirect
+from django.http import HttpResponseRedirect, Http404
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
+from django.utils.http import urlquote_plus
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, logout
from django.http import get_host
import types
+import datetime
-from forum.models import AuthKeyUserAssociation
-from forum.authentication.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm
+from forum.models import AuthKeyUserAssociation, ValidationHash
+from forum.authentication.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm, \
+ TemporaryLoginRequestForm, ChangePasswordForm, SetPasswordForm
+from forum.utils.email import send_email
from forum.authentication.base import InvalidAuthentication
from forum.authentication import AUTH_PROVIDERS
@@ -23,6 +27,7 @@ def signin_page(request, action=None):
request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/')
else:
request.session['on_signin_action'] = action
+ request.session['on_signin_url'] = reverse('auth_action_signin', kwargs={'action': action})
all_providers = [provider.context for provider in AUTH_PROVIDERS.values()]
@@ -104,8 +109,13 @@ def process_provider_signin(request, provider):
except:
uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider)
uassoc.save()
- request.session['auth_error'] = _("These new credentials are now associated with your account.")
+ request.user.message_set.create(message=_('The new credentials are now associated with your account'))
+ return HttpResponseRedirect(reverse('user_authsettings'))
+
return HttpResponseRedirect(reverse('auth_signin'))
+ else:
+ if isinstance(assoc_key, (type, User)):
+ return login_and_forward(request, assoc_key)
try:
assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
@@ -124,12 +134,22 @@ def external_register(request):
email_feeds_form = SimpleEmailSubscribeForm(request.POST)
if (form1.is_valid() and email_feeds_form.is_valid()):
- tmp_pwd = User.objects.make_random_password()
- user_ = User.objects.create_user(form1.cleaned_data['username'],
- form1.cleaned_data['email'], tmp_pwd)
-
+ user_ = User(username=form1.cleaned_data['username'], email=form1.cleaned_data['email'])
+ user_.email_isvalid = request.session.get('auth_validated_email', '') == form1.cleaned_data['email']
user_.set_unusable_password()
+ user_.save()
+
+ if not user_.email_isvalid:
+ send_validation_email(user_)
+
+ try:
+ assoc_key = request.session['assoc_key']
+ auth_provider = request.session['auth_provider']
+ except:
+ request.session['auth_error'] = _("Oops, something went wrong in the middle of this process. Please try again.")
+ return HttpResponseRedirect(request.session.get('on_signin_url', reverse('auth_signin')))
+
uassoc = AuthKeyUserAssociation(user=user_, key=request.session['assoc_key'], provider=request.session['auth_provider'])
uassoc.save()
@@ -137,7 +157,11 @@ def external_register(request):
del request.session['assoc_key']
del request.session['auth_provider']
- return login_and_forward(request, user_)
+
+ if user_.email_isvalid:
+ return login_and_forward(request, user_)
+ else:
+ return HttpResponseRedirect(reverse('index'))
else:
provider_class = AUTH_PROVIDERS[request.session['auth_provider']].consumer
user_data = provider_class.get_user_data(request.session['assoc_key'])
@@ -148,6 +172,9 @@ def external_register(request):
if not email:
email = request.session.get('auth_email_request', '')
+ if email:
+ request.session['auth_validated_email'] = email
+
form1 = SimpleRegistrationForm(initial={
'next': '/',
'username': username,
@@ -165,6 +192,121 @@ def external_register(request):
'gravatar_faq_url':reverse('faq') + '#gravatar',
}, context_instance=RequestContext(request))
+def request_temp_login(request):
+ if request.method == 'POST':
+ form = TemporaryLoginRequestForm(request.POST)
+
+ if form.is_valid():
+ user = form.user_cache
+
+ try:
+ hash = get_object_or_404(ValidationHash, user=user, type='templogin')
+ if hash.expiration < datetime.datetime.now():
+ hash.delete()
+ return request_temp_login(request)
+ except:
+ hash = ValidationHash.objects.create_new(user, 'templogin', [user.id])
+
+ send_email(_("Temporary login link"), [user.email], "auth/temp_login_email.html", {
+ 'temp_login_code': hash,
+ 'user': user
+ })
+
+ request.user.message_set.create(message=_("An email has been sent with your temporary login key"))
+
+ return HttpResponseRedirect(reverse('index'))
+ else:
+ form = TemporaryLoginRequestForm()
+
+ return render_to_response(
+ 'auth/temp_login_request.html', {'form': form},
+ context_instance=RequestContext(request))
+
+def temp_signin(request, user, code):
+ user = get_object_or_404(User, id=user)
+
+ if (ValidationHash.objects.validate(code, user, 'templogin', [user.id])):
+ return login_and_forward(request, user, reverse('user_authsettings'),
+ _("You are logged in with a temporary access key, please take the time to fix your issue with authentication."))
+ else:
+ raise Http404()
+
+def send_validation_email(user):
+ hash = ValidationHash.objects.create_new(user, 'email', [user.email])
+ send_email(_("Email Validation"), [user.email], "auth/email_validation.html", {
+ 'validation_code': hash,
+ 'user': user
+ })
+
+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, None, _("Thank you, your email is now validated."))
+ else:
+ raise Http404()
+
+@login_required
+def auth_settings(request):
+ """
+ change password view.
+
+ url : /changepw/
+ template: authopenid/changepw.html
+ """
+ user_ = request.user
+ auth_keys = user_.auth_keys.all()
+
+ if user_.has_usable_password():
+ FormClass = ChangePasswordForm
+ else:
+ FormClass = SetPasswordForm
+
+ if request.POST:
+ form = FormClass(request.POST, user=user_)
+ if form.is_valid():
+ if user_.has_usable_password():
+ request.user.message_set.create(message=_("Your password was changed"))
+ else:
+ request.user.message_set.create(message=_("New password set"))
+ form = ChangePasswordForm(user=user_)
+
+ user_.set_password(form.cleaned_data['password1'])
+ user_.save()
+ return HttpResponseRedirect(reverse('user_authsettings'))
+ else:
+ form = FormClass(user=user_)
+
+ auth_keys_list = []
+
+ for k in auth_keys:
+ provider = AUTH_PROVIDERS.get(k.provider, None)
+
+ if provider is not None:
+ name = "%s: %s" % (provider.context.human_name, provider.context.readable_key(k))
+ else:
+ from forum.authentication.base import ConsumerTemplateContext
+ "unknown: %s" % ConsumerTemplateContext.readable_key(k)
+
+ auth_keys_list.append({
+ 'name': name,
+ 'id': k.id
+ })
+
+ return render_to_response('auth/auth_settings.html', {
+ 'form': form,
+ 'has_password': user_.has_usable_password(),
+ 'auth_keys': auth_keys_list,
+ }, context_instance=RequestContext(request))
+
+def remove_external_provider(request, id):
+ association = get_object_or_404(AuthKeyUserAssociation, id=id)
+ request.user.message_set.create(message=_("You removed the association with %s") % association.provider)
+ association.delete()
+ return HttpResponseRedirect(reverse('user_authsettings'))
+
def newquestion_signin_action(user):
question = Question.objects.filter(author=user).order_by('-added_at')[0]
return question.get_absolute_url()
@@ -178,7 +320,7 @@ POST_SIGNIN_ACTIONS = {
'newanswer': newanswer_signin_action,
}
-def login_and_forward(request, user):
+def login_and_forward(request, user, forward=None, message=None):
old_session = request.session.session_key
user.backend = "django.contrib.auth.backends.ModelBackend"
login(request, user)
@@ -186,19 +328,24 @@ def login_and_forward(request, user):
from forum.models import user_logged_in
user_logged_in.send(user=user,session_key=old_session,sender=None)
- redirect = request.session.get('on_signin_url', None)
-
- if not redirect:
+ if not forward:
signin_action = request.session.get('on_signin_action', None)
if not signin_action:
- redirect = reverse('index')
+ forward = request.session.get('on_signin_url', None)
+
+ if not forward:
+ forward = reverse('index')
else:
try:
- redirect = POST_SIGNIN_ACTIONS[signin_action](user)
+ forward = POST_SIGNIN_ACTIONS[signin_action](user)
except:
- redirect = reverse('index')
+ forward = reverse('index')
+
+ if message is None:
+ message = _("Welcome back %s, you are now logged in") % user.username
- return HttpResponseRedirect(redirect)
+ request.user.message_set.create(message=message)
+ return HttpResponseRedirect(forward)
@login_required
def signout(request):
diff --git a/forum/views/readers.py b/forum/views/readers.py
index c4ce5ddf..938fa133 100644
--- a/forum/views/readers.py
+++ b/forum/views/readers.py
@@ -14,6 +14,7 @@ from django.utils.translation import ugettext as _
from django.template.defaultfilters import slugify
from django.core.urlresolvers import reverse
from django.utils.datastructures import SortedDict
+from django.views.decorators.cache import cache_page
from forum.utils.html import sanitize_html
from markdown2 import Markdown
diff --git a/forum/views/users.py b/forum/views/users.py
index baa8090b..ff92803c 100755
--- a/forum/views/users.py
+++ b/forum/views/users.py
@@ -947,44 +947,13 @@ USER_TEMPLATE_VIEWS = (
)
)
-def user(request, id):
+def user(request, id, slug=None):
sort = request.GET.get('sort', 'stats')
user_view = dict((v.id, v) for v in USER_TEMPLATE_VIEWS).get(sort, USER_TEMPLATE_VIEWS[0])
from forum.views import users
func = user_view.view_func
return func(request, id, user_view)
-
-@login_required
-def changepw(request):
- """
- change password view.
-
- url : /changepw/
- template: authopenid/changepw.html
- """
- logging.debug('')
- user_ = request.user
-
- if not user_.has_usable_password():
- raise Http404
-
- if request.POST:
- form = ChangePasswordForm(request.POST, user=user_)
- if form.is_valid():
- user_.set_password(form.cleaned_data['password1'])
- user_.save()
- msg = _("Password changed.")
- redirect = "%s?msg=%s" % (
- reverse('user_account_settings'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
- else:
- form = ChangePasswordForm(user=user_)
-
- return render_to_response('changepw.html', {'form': form },
- context_instance=RequestContext(request))
-
@login_required
def account_settings(request):
"""