diff options
Diffstat (limited to 'forum')
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 Binary files differnew file mode 100644 index 00000000..35c9e149 --- /dev/null +++ b/forum/skins/default/media/images/favicon.ico 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 }}">●</span> {{ 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 }}">●</span> {{ 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 }}">●</span> {{ award.name }}</a><span class="tag-number"> ✕ {{ award.count|intcomma }}</span><br/> + <a href="{% url badges %}{{award.id}}/{{award.name}}" title="{{ award.description }}" class="medal"><span class="badge{{ award.type }}">●</span> {{ award.name }}</a><span class="tag-number"> × {{ 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): """ |