From 2f4bfbd5c5d61ed6ba1e1f956c04a74207c94939 Mon Sep 17 00:00:00 2001
From: Rick Ross
Date: Mon, 28 Dec 2009 23:27:09 -0500
Subject: Handled a case where votes is null for unauthenticated user
---
forum/models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/forum/models.py b/forum/models.py
index bceced1a..c3b89ce9 100644
--- a/forum/models.py
+++ b/forum/models.py
@@ -482,7 +482,7 @@ class Answer(models.Model):
def get_user_vote(self, user):
votes = self.votes.filter(user=user)
- if votes.count() > 0:
+ if votes and votes.count() > 0:
return votes[0]
else:
return None
--
cgit v1.2.3-1-g7c22
From c7340cf0280b75d17095a11b2d25c9a92dd7edaa Mon Sep 17 00:00:00 2001
From: Rick Ross
Date: Thu, 31 Dec 2009 22:40:14 -0500
Subject: minor fix for python 2.4 compatibility
---
forum/templatetags/extra_tags.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py
index b2199284..0694e5d4 100644
--- a/forum/templatetags/extra_tags.py
+++ b/forum/templatetags/extra_tags.py
@@ -244,9 +244,9 @@ def diff_date(date, limen=2):
if days > 2:
if date.year == now.year:
- return date.strftime(_("%b %d at %H:%M"))
+ return date.strftime("%b %d at %H:%M")
else:
- return date.strftime(_("%b %d '%y at %H:%M"))
+ return date.strftime("%b %d '%y at %H:%M")
elif days == 2:
return _('2 days ago')
elif days == 1:
--
cgit v1.2.3-1-g7c22
From 5ca23a8d9acff3306e39e3cf99ceea1aa03e80d6 Mon Sep 17 00:00:00 2001
From: Rick Ross
Date: Sat, 16 Jan 2010 08:29:35 -0500
Subject: modified try-except-finally blocks for python 2.4 compatibility
---
forum/management/commands/clean_award_badges.py | 7 ++--
forum/management/commands/multi_award_badges.py | 45 +++++++++++++------------
forum/management/commands/once_award_badges.py | 23 +++++++------
forum/management/commands/send_email_alerts.py | 7 ++--
forum/management/commands/subscribe_everyone.py | 7 ++--
5 files changed, 47 insertions(+), 42 deletions(-)
diff --git a/forum/management/commands/clean_award_badges.py b/forum/management/commands/clean_award_badges.py
index df3d2917..117e3a5f 100644
--- a/forum/management/commands/clean_award_badges.py
+++ b/forum/management/commands/clean_award_badges.py
@@ -21,9 +21,10 @@ from forum.models import *
class Command(NoArgsCommand):
def handle_noargs(self, **options):
try:
- self.clean_awards()
- except Exception, e:
- print e
+ try:
+ self.clean_awards()
+ except Exception, e:
+ print e
finally:
connection.close()
diff --git a/forum/management/commands/multi_award_badges.py b/forum/management/commands/multi_award_badges.py
index 723a8cec..c6dbc250 100644
--- a/forum/management/commands/multi_award_badges.py
+++ b/forum/management/commands/multi_award_badges.py
@@ -82,27 +82,28 @@ TYPE_ACTIVITY_USER_FULL_UPDATED = 17
class Command(BaseCommand):
def handle_noargs(self, **options):
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
+ 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()
@@ -317,7 +318,7 @@ class Command(BaseCommand):
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=objet_id)
+ award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
award.save()
if update_auditted:
diff --git a/forum/management/commands/once_award_badges.py b/forum/management/commands/once_award_badges.py
index 03982c79..8c913348 100644
--- a/forum/management/commands/once_award_badges.py
+++ b/forum/management/commands/once_award_badges.py
@@ -94,17 +94,18 @@ BADGE_AWARD_TYPE_FIRST = {
class Command(BaseCommand):
def handle_noargs(self, **options):
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
+ 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()
diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py
index 777381ec..f5974e6b 100644
--- a/forum/management/commands/send_email_alerts.py
+++ b/forum/management/commands/send_email_alerts.py
@@ -14,9 +14,10 @@ from utils.odict import OrderedDict
class Command(NoArgsCommand):
def handle_noargs(self,**options):
try:
- self.send_email_alerts()
- except Exception, e:
- print e
+ try:
+ self.send_email_alerts()
+ except Exception, e:
+ print e
finally:
connection.close()
diff --git a/forum/management/commands/subscribe_everyone.py b/forum/management/commands/subscribe_everyone.py
index 3f8da9ec..f5cbf8eb 100644
--- a/forum/management/commands/subscribe_everyone.py
+++ b/forum/management/commands/subscribe_everyone.py
@@ -11,9 +11,10 @@ import settings
class Command(NoArgsCommand):
def handle_noargs(self,**options):
try:
- self.subscribe_everyone()
- except Exception, e:
- print e
+ try:
+ self.subscribe_everyone()
+ except Exception, e:
+ print e
finally:
connection.close()
--
cgit v1.2.3-1-g7c22
From 3bbb1a6d091bff45172e44e135d684ec3ccb8b4a Mon Sep 17 00:00:00 2001
From: Rick Ross
Date: Sat, 16 Jan 2010 10:33:48 -0500
Subject: fixes a bug that caused an exception when you clicked a badge to see
who has it
---
forum/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/forum/views.py b/forum/views.py
index 20ca2f6a..ca656b98 100644
--- a/forum/views.py
+++ b/forum/views.py
@@ -2091,7 +2091,7 @@ def badge(request, id):
tables=['award', 'auth_user'],
where=['badge_id=%s AND user_id=auth_user.id'],
params=[id]
- ).values('id').distinct()
+ ).distinct('id')
return render_to_response('badge.html', {
'awards' : awards,
--
cgit v1.2.3-1-g7c22
From 456e24961954c3c34f5ad2d0ce035440cb6a671e Mon Sep 17 00:00:00 2001
From: Rick Ross
Date: Sat, 16 Jan 2010 17:45:40 -0500
Subject: added a short name setting for the app and modified the default about
page to use it
---
context.py | 1 +
settings_local.py.dist | 31 +++++++++++++++++--------------
templates/about.html | 30 ++++++++++++------------------
3 files changed, 30 insertions(+), 32 deletions(-)
diff --git a/context.py b/context.py
index 26d326a7..ca6ca163 100644
--- a/context.py
+++ b/context.py
@@ -2,6 +2,7 @@ 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,
diff --git a/settings_local.py.dist b/settings_local.py.dist
index 5447e517..14b22e5a 100644
--- a/settings_local.py.dist
+++ b/settings_local.py.dist
@@ -3,7 +3,7 @@ import os.path
from django.utils.translation import ugettext as _
SITE_SRC_ROOT = os.path.dirname(__file__)
-LOG_FILENAME = 'django.lanai.log'
+LOG_FILENAME = 'django.osqa.log'
#for logging
import logging
@@ -18,10 +18,12 @@ DEBUG = False
TEMPLATE_DEBUG = DEBUG
INTERNAL_IPS = ('127.0.0.1',)
-DATABASE_NAME = 'cnprog' # Or path to database file if using sqlite3.
+DATABASE_NAME = 'osqa' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_ENGINE = 'mysql' #mysql, etc
+DATABASE_HOST = ''
+DATABASE_PORT = ''
#Moved from settings.py for better organization. (please check it up to clean up settings.py)
@@ -30,13 +32,13 @@ SERVER_EMAIL = ''
DEFAULT_FROM_EMAIL = ''
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
-EMAIL_SUBJECT_PREFIX = '[CNPROG] '
-EMAIL_HOST='cnprog.com'
+EMAIL_SUBJECT_PREFIX = '[OSQA] '
+EMAIL_HOST='osqa.net'
EMAIL_PORT='25'
EMAIL_USE_TLS=False
#LOCALIZATIONS
-TIME_ZONE = 'America/Tijuana'
+TIME_ZONE = 'America/New_York'
###########################
#
@@ -48,11 +50,12 @@ FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string
#OTHER SETTINGS
-APP_TITLE = u'CNPROG Q&A Forum'
-APP_KEYWORDS = u'CNPROG,forum,community'
+APP_TITLE = u'OSQA: Open Source Q&A Forum'
+APP_SHORT_NAME = u'OSQA'
+APP_KEYWORDS = u'OSQA,CNPROG,forum,community'
APP_DESCRIPTION = u'Ask and answer questions.'
APP_INTRO = u'
Ask and answer questions, make the world better!
'
-APP_COPYRIGHT = 'Copyright CNPROG, 2009. Some rights reserved under creative commons license.'
+APP_COPYRIGHT = 'Copyright OSQA, 2009. Some rights reserved under creative commons license.'
LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
GREETING_URL = LOGIN_URL #may be url of "faq" page or "about", etc
@@ -61,25 +64,25 @@ LANGUAGE_CODE = 'en'
EMAIL_VALIDATION = 'off' #string - on|off
MIN_USERNAME_LENGTH = 1
EMAIL_UNIQUE = False
-APP_URL = 'http://cnprog.com' #used by email notif system and RSS
-GOOGLE_SITEMAP_CODE = '55uGNnQVJW8p1bbXeF/Xbh9I7nZBM/wLhRz6N/I1kkA='
+APP_URL = 'http://osqa.net' #used by email notif system and RSS
+GOOGLE_SITEMAP_CODE = ''
GOOGLE_ANALYTICS_KEY = ''
BOOKS_ON = False
WIKI_ON = True
USE_EXTERNAL_LEGACY_LOGIN = False
-EXTERNAL_LEGACY_LOGIN_HOST = 'login.cnprog.com'
+EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.net'
EXTERNAL_LEGACY_LOGIN_PORT = 80
-EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'CNPROG'
+EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'OSQA'
FEEDBACK_SITE_URL = None #None or url
DJANGO_VERSION = 1.1
RESOURCE_REVISION=4
-USE_SPHINX_SEARCH = True #if True all SPHINX_* settings are required
+USE_SPHINX_SEARCH = False #if True all SPHINX_* settings are required
#also sphinx search engine and djangosphinxs app must be installed
#sample sphinx configuration file is /sphinx/sphinx.conf
SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
-SPHINX_SEARCH_INDICES=('cnprog',) #a tuple of index names remember about a comma after the
+SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
#last item, especially if you have just one :)
SPHINX_SERVER='localhost'
SPHINX_PORT=3312
diff --git a/templates/about.html b/templates/about.html
index 2ca75500..ec4b6a73 100644
--- a/templates/about.html
+++ b/templates/about.html
@@ -12,30 +12,24 @@
-
NMR Wiki Q&A is a collaboratively edited question
- and answer site created for the Magnetic Resonance community, i.e. those people who
- work in the fields of NMR, EPR, MRI, etc.
- NMR Wiki Q&A is affiliated with NMR Wiki -
- the public wiki knowledge base about the Magnetic Resonance, which currently counts ~300 registered users. The most useful information collected here
- will be further distilled on the wiki site.
-
+
{{settings.APP_SHORT_NAME}} is a collaboratively edited question and answer site created with
+ OSQA: The Open Source Q&A System.
+
Here you can ask and answer questions, comment
and vote for the questions of others and their answers. Both questions and answers
can be revised and improved. Questions can be tagged with
- the relevant keywords to simplify future access and organize the accumulated material.
-
+ the relevant keywords to simplify future access and organize the accumulated material.
-
This Q&A site is moderated by its members, hopefully - including yourself!
+
This OSQA site is moderated by its members, hopefully - including yourself!
Moderation rights are gradually assigned to the site users based on the accumulated "reputation"
points. These points are added to the users account when others vote for his/her questions or answers.
- These points (very) roughly reflect the level of trust of the community.
-
-
No points are necessary to ask or answer the questions - so please -
- join us!
-
{% endblock %}
--
cgit v1.2.3-1-g7c22
From 180b3210d76a72451b93a0741ffb35c90d861c9f Mon Sep 17 00:00:00 2001
From: Rick Ross
Date: Sat, 16 Jan 2010 18:05:36 -0500
Subject: fix to make update profile work
---
django_authopenid/forms.py | 3 +++
forum/forms.py | 1 +
2 files changed, 4 insertions(+)
diff --git a/django_authopenid/forms.py b/django_authopenid/forms.py
index d4482751..6781401e 100644
--- a/django_authopenid/forms.py
+++ b/django_authopenid/forms.py
@@ -88,6 +88,9 @@ class UserNameField(forms.CharField):
username = super(UserNameField,self).clean(username.strip())
if self.skip_clean == True:
return username
+ if hasattr(self, 'user_instance'):
+ if username == self.user_instance.username:
+ return username
if not username_re.search(username):
raise forms.ValidationError(self.error_messages['invalid'])
if username in self.RESERVED_NAMES:
diff --git a/forum/forms.py b/forum/forms.py
index ad2c5bac..376f5ddf 100644
--- a/forum/forms.py
+++ b/forum/forms.py
@@ -205,6 +205,7 @@ class EditUserForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super(EditUserForm, self).__init__(*args, **kwargs)
self.fields['username'].initial = user.username
+ self.fields['username'].user_instance = user
self.fields['email'].initial = user.email
self.fields['realname'].initial = user.real_name
self.fields['website'].initial = user.website
--
cgit v1.2.3-1-g7c22
From 58674cd85b269009fa80ec69c073a96ee1ceefae Mon Sep 17 00:00:00 2001
From: hrcerqueira
Date: Wed, 20 Jan 2010 15:12:38 +0000
Subject: On enabling/full text search, users should add/remove djangosphinx
from the INSTALLED_APPS setting.
---
INSTALL | 598 ++++++++++++++++++++++++++++++------------------------------
settings.py | 142 +++++++--------
2 files changed, 372 insertions(+), 368 deletions(-)
diff --git a/INSTALL b/INSTALL
index 7de10871..fe41befe 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,297 +1,301 @@
-CONTENTS
-------------------
-A. PREREQUISITES
-B. INSTALLATION
- 1. Settings file
- 2. Database
- 3. Running CNPROG in the development server
- 4. Installation under Apache/WSGI
- 5. Full text search
- 6. Email subscriptions
- 7. Sitemap
- 8. Miscellaneous
-C. CONFIGURATION PARAMETERS (settings_local.py)
-D. CUSTOMIZATION
-
-
-A. PREREQUISITES
------------------------------------------------
-0. We recommend you to use python-setuptools to install pre-requirement libraries.
-If you haven't installed it, please try to install it first.
-e.g, sudo apt-get install python-setuptools
-
-1. Python2.5/2.6, MySQL, Django v1.0/1.1
-Note: email subscription sender job requires Django 1.1, everything else works with 1.0
-Make sure mysql for python provider has been installed.
-sudo easy_install mysql-python
-
-2. Python-openid v2.2
-http://openidenabled.com/python-openid/
-sudo easy_install python-openid
-
-4. html5lib
-http://code.google.com/p/html5lib/
-Used for HTML sanitizer
-sudo easy_install html5lib
-
-5. Markdown2
-http://code.google.com/p/python-markdown2/
-sudo easy_install markdown2
-
-6. Django Debug Toolbar
-http://github.com/robhudson/django-debug-toolbar/tree/master
-
-7. djangosphinx (optional - for full text questions+answer+tag)
-http://github.com/dcramer/django-sphinx/tree/master/djangosphinx
-
-8. sphinx search engine (optional, works together with djangosphinx)
-http://sphinxsearch.com/downloads.html
-
-NOTES: django_authopenid is included into CNPROG code
-and is significantly modified. http://code.google.com/p/django-authopenid/
-no need to install this library
-
-B. INSTALLATION
------------------------------------------------
-0. Make sure you have all above python libraries installed.
-
- make cnprog installation server-readable on Linux command might be:
- chown -R yourlogin:apache /path/to/CNPROG
-
- directories templates/upfiles and log must be server writable
-
- on Linux type chmod
- chmod -R g+w /path/to/CNPROG/upfiles
- chmod -R g+w /path/to/log
-
- above it is assumed that webserver runs under group named "apache"
-
-1. Settings file
-
-Copy settings_local.py.dist to settings_local.py and
-update all your settings. Check settings.py and update
-it as well if necessory.
-Section C explains configuration paramaters.
-
-2. Database
-
-Prepare your database by using the same database/account
-configuration from above.
-e.g,
-create database cnprog DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci;
-grant all on cnprog.* to 'cnprog'@'localhost';
-And then run "python manage.py syncdb" to synchronize your database.
-
-3. Running CNPROG on the development server
-
-Run "python manage.py runserver" to startup django
-development environment.
-(Under Linux you can use command "python manage.py runserver `hostname -i`:8000",
-where you can use any other available number for the port)
-
-you might want to have DEBUG=True in the beginning of settings.py
-when using the test server
-
-4. Installation under Apache/WSGI
-
-4.1 Prepare wsgi script
-
-Make a file readable by your webserver with the following content:
-
----------
-import os
-import sys
-
-sys.path.insert(0,'/one/level/above') #insert to make sure that forum will be found
-sys.path.append('/one/level/above/CNPROG') #maybe this is not necessary
-os.environ['DJANGO_SETTINGS_MODULE'] = 'CNPROG.settings'
-import django.core.handlers.wsgi
-application = django.core.handlers.wsgi.WSGIHandler()
------------
-
-insert method is used for path because if the forum directory name
-is by accident the same as some other python module
-you wull see strange errors - forum won't be found even though
-it's in the python path. for example using name "test" is
-not a good idea - as there is a module with such name
-
-
-4.2 Configure webserver
-Settings below are not perfect but may be a good starting point
-
----------
-WSGISocketPrefix /path/to/socket/sock #must be readable and writable by apache
-WSGIPythonHome /usr/local #must be readable by apache
-WSGIPythonEggs /var/python/eggs #must be readable and writable by apache
-
-#NOTE: all urs below will need to be adjusted if
-#settings.FORUM_SCRIPT_ALIAS !='' (e.g. = 'forum/')
-#this allows "rooting" forum at http://example.com/forum, if you like
-
- ServerAdmin forum@example.com
- DocumentRoot /path/to/cnprog
- ServerName example.com
-
- #run mod_wsgi process for django in daemon mode
- #this allows avoiding confused timezone settings when
- #another application runs in the same virtual host
- WSGIDaemonProcess CNPROG
- WSGIProcessGroup CNPROG
-
- #force all content to be served as static files
- #otherwise django will be crunching images through itself wasting time
- Alias /content/ /path/to/cnprog/templates/content/
- AliasMatch /([^/]*\.css) /path/to/cnprog/templates/content/style/$1
-
- Order deny,allow
- Allow from all
-
-
- #this is your wsgi script described in the prev section
- WSGIScriptAlias / /path/to/cnprog/cnprog.wsgi
-
- #this will force admin interface to work only
- #through https (optional)
- #"nimda" is the secret spelling of "admin" ;)
-
- RewriteEngine on
- RewriteRule /nimda(.*)$ https://example.com/nimda$1 [L,R=301]
-
- CustomLog /var/log/httpd/CNPROG/access_log common
- ErrorLog /var/log/httpd/CNPROG/error_log
-
-#(optional) run admin interface under https
-
- ServerAdmin forum@example.com
- DocumentRoot /path/to/cnrpog
- ServerName example.com
- SSLEngine on
- SSLCertificateFile /path/to/ssl-certificate/server.crt
- SSLCertificateKeyFile /path/to/ssl-certificate/server.key
- WSGIScriptAlias / /path/to/cnprogcnprog.wsgi
- CustomLog /var/log/httpd/CNPROG/access_log common
- ErrorLog /var/log/httpd/CNPROG/error_log
- DirectoryIndex index.html
-
--------------
-
-5. Full text search (using sphinx search)
- Currently full text search works only with sphinx search engine
- Sphinx at this time supports only MySQL and PostgreSQL databases
- to enable this, install sphinx search engine and djangosphinx
-
- configure sphinx, sample configuration can be found in
- sphinx/sphinx.conf file usually goes somewhere in /etc tree
-
- build cnprog index first time manually
-
- % indexer --config /path/to/sphinx.conf --index cnprog
-
- setup cron job to rebuild index periodically with command
- your crontab entry may be something like
-
- 0 9,15,21 * * * /usr/local/bin/indexer --config /etc/sphinx/sphinx.conf --all --rotate >/dev/null 2>&1
- adjust it as necessary this one will reindex three times a day at 9am 3pm and 9pm
-
- if your forum grows very big ( good luck with that :) you'll
- need to two search indices one diff index and one main
- please refer to online sphinx search documentation for the information
- on the subject http://sphinxsearch.com/docs/
-
- in settings_local.py set
- USE_SPHINX_SEARCH=True
- adjust other settings that have SPHINX_* prefix accordingly
- remember that there must be trailing comma in parentheses for
- SHPINX_SEARCH_INDICES tuple - particlarly with just one item!
-
-6. Email subscriptions
-
- This function at the moment requires Django 1.1
-
- edit paths in the file cron/send_email_alerts
- set up a cron job to call cron/send_email_alerts once or twice a day
- subscription sender may be tested manually in shell
- by calling cron/send_email_alerts
-
-7. Sitemap
-Sitemap will be available at /sitemap.xml
-e.g yoursite.com/forum/sitemap.xml
-
-google will be pinged each time question, answer or
-comment is saved or a question deleted
-
-for this to be useful - do register you sitemap with Google at
-https://www.google.com/webmasters/tools/
-
-8. Miscellaneous
-
-There are some demo scripts under sql_scripts folder,
-including badges and test accounts for CNProg.com. You
-don't need them to run your sample.
-
-C. CONFIGURATION PARAMETERS
-
-#the only parameter that needs to be touched in settings.py is
-DEBUG=False #set to True to enable debug mode
-
-#all forum parameters are set in file settings_local.py
-
-LOG_FILENAME = 'cnprog.log' #where logging messages should go
-DATABASE_NAME = 'cnprog' # Or path to database file if using sqlite3.
-DATABASE_USER = '' # Not used with sqlite3.
-DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_ENGINE = 'mysql' #mysql, etc
-SERVER_EMAIL = ''
-DEFAULT_FROM_EMAIL = ''
-EMAIL_HOST_USER = ''
-EMAIL_HOST_PASSWORD = '' #not necessary if mailserver is run on local machine
-EMAIL_SUBJECT_PREFIX = '[CNPROG] '
-EMAIL_HOST='cnprog.com'
-EMAIL_PORT='25'
-EMAIL_USE_TLS=False
-TIME_ZONE = 'America/Tijuana'
-APP_TITLE = u'CNPROG Q&A Forum' #title of your forum
-APP_KEYWORDS = u'CNPROG,forum,community' #keywords for search engines
-APP_DESCRIPTION = u'Ask and answer questions.' #site description for searche engines
-APP_INTRO = u'
Ask and answer questions, make the world better!
' #slogan that goes to front page in logged out mode
-APP_COPYRIGHT = '' #copyright message
-
-#if you set FORUM_SCRIPT_ALIAS= 'forum/'
-#then CNPROG will run at url http://example.com/forum
-#FORUM_SCRIPT_ALIAS cannot have leading slash, otherwise it can be set to anything
-FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string
-
-LANGUAGE_CODE = 'en' #forum language (see language instructions on the wiki)
-EMAIL_VALIDATION = 'off' #string - on|off
-MIN_USERNAME_LENGTH = 1
-EMAIL_UNIQUE = False #if True, email addresses must be unique in all accounts
-APP_URL = 'http://cnprog.com' #used by email notif system and RSS
-GOOGLE_SITEMAP_CODE = '' #code for google site crawler (look up google webmaster tools)
-GOOGLE_ANALYTICS_KEY = '' #key to enable google analytics on this site
-BOOKS_ON = False #if True - books tab will be on
-WIKI_ON = True #if False - community wiki feature is disabled
-
-#experimental - allow password login through external site
-#must implement django_authopenid/external_login.py
-#included prototype external_login works with Mediawiki
-USE_EXTERNAL_LEGACY_LOGIN = True #if false CNPROG uses it's own login/password
-EXTERNAL_LEGACY_LOGIN_HOST = 'login.cnprog.com'
-EXTERNAL_LEGACY_LOGIN_PORT = 80
-EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'CNPROG'
-
-FEEDBACK_SITE_URL = None #None or url
-LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
-
-DJANGO_VERSION = 1.1 #must be either 1.0 or 1.1
-RESOURCE_REVISION=4 #increment when you update media files - clients will be forced to load new version
-
-D. Customization
-
-Other than settings_local.py the following will most likely need customization:
-* locale/*/django.po - language files that may also contain your site-specific messages
- if you want to start with english messages file - look for words like "forum" and
- "CNPROG" in the msgstr lines
-* templates/header.html and templates/footer.html may contain extra links
-* templates/about.html - a place to explain for is your forum for
-* templates/faq.html - put answers to users frequent questions
-* templates/content/style/style.css - modify style sheet to add disctinctive look to your forum
+CONTENTS
+------------------
+A. PREREQUISITES
+B. INSTALLATION
+ 1. Settings file
+ 2. Database
+ 3. Running CNPROG in the development server
+ 4. Installation under Apache/WSGI
+ 5. Full text search
+ 6. Email subscriptions
+ 7. Sitemap
+ 8. Miscellaneous
+C. CONFIGURATION PARAMETERS (settings_local.py)
+D. CUSTOMIZATION
+
+
+A. PREREQUISITES
+-----------------------------------------------
+0. We recommend you to use python-setuptools to install pre-requirement libraries.
+If you haven't installed it, please try to install it first.
+e.g, sudo apt-get install python-setuptools
+
+1. Python2.5/2.6, MySQL, Django v1.0/1.1
+Note: email subscription sender job requires Django 1.1, everything else works with 1.0
+Make sure mysql for python provider has been installed.
+sudo easy_install mysql-python
+
+2. Python-openid v2.2
+http://openidenabled.com/python-openid/
+sudo easy_install python-openid
+
+4. html5lib
+http://code.google.com/p/html5lib/
+Used for HTML sanitizer
+sudo easy_install html5lib
+
+5. Markdown2
+http://code.google.com/p/python-markdown2/
+sudo easy_install markdown2
+
+6. Django Debug Toolbar
+http://github.com/robhudson/django-debug-toolbar/tree/master
+
+7. djangosphinx (optional - for full text questions+answer+tag)
+http://github.com/dcramer/django-sphinx/tree/master/djangosphinx
+
+8. sphinx search engine (optional, works together with djangosphinx)
+http://sphinxsearch.com/downloads.html
+
+NOTES: django_authopenid is included into CNPROG code
+and is significantly modified. http://code.google.com/p/django-authopenid/
+no need to install this library
+
+B. INSTALLATION
+-----------------------------------------------
+0. Make sure you have all above python libraries installed.
+
+ make cnprog installation server-readable on Linux command might be:
+ chown -R yourlogin:apache /path/to/CNPROG
+
+ directories templates/upfiles and log must be server writable
+
+ on Linux type chmod
+ chmod -R g+w /path/to/CNPROG/upfiles
+ chmod -R g+w /path/to/log
+
+ above it is assumed that webserver runs under group named "apache"
+
+1. Settings file
+
+Copy settings_local.py.dist to settings_local.py and
+update all your settings. Check settings.py and update
+it as well if necessory.
+Section C explains configuration paramaters.
+
+2. Database
+
+Prepare your database by using the same database/account
+configuration from above.
+e.g,
+create database cnprog DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci;
+grant all on cnprog.* to 'cnprog'@'localhost';
+And then run "python manage.py syncdb" to synchronize your database.
+
+3. Running CNPROG on the development server
+
+Run "python manage.py runserver" to startup django
+development environment.
+(Under Linux you can use command "python manage.py runserver `hostname -i`:8000",
+where you can use any other available number for the port)
+
+you might want to have DEBUG=True in the beginning of settings.py
+when using the test server
+
+4. Installation under Apache/WSGI
+
+4.1 Prepare wsgi script
+
+Make a file readable by your webserver with the following content:
+
+---------
+import os
+import sys
+
+sys.path.insert(0,'/one/level/above') #insert to make sure that forum will be found
+sys.path.append('/one/level/above/CNPROG') #maybe this is not necessary
+os.environ['DJANGO_SETTINGS_MODULE'] = 'CNPROG.settings'
+import django.core.handlers.wsgi
+application = django.core.handlers.wsgi.WSGIHandler()
+-----------
+
+insert method is used for path because if the forum directory name
+is by accident the same as some other python module
+you wull see strange errors - forum won't be found even though
+it's in the python path. for example using name "test" is
+not a good idea - as there is a module with such name
+
+
+4.2 Configure webserver
+Settings below are not perfect but may be a good starting point
+
+---------
+WSGISocketPrefix /path/to/socket/sock #must be readable and writable by apache
+WSGIPythonHome /usr/local #must be readable by apache
+WSGIPythonEggs /var/python/eggs #must be readable and writable by apache
+
+#NOTE: all urs below will need to be adjusted if
+#settings.FORUM_SCRIPT_ALIAS !='' (e.g. = 'forum/')
+#this allows "rooting" forum at http://example.com/forum, if you like
+
+ ServerAdmin forum@example.com
+ DocumentRoot /path/to/cnprog
+ ServerName example.com
+
+ #run mod_wsgi process for django in daemon mode
+ #this allows avoiding confused timezone settings when
+ #another application runs in the same virtual host
+ WSGIDaemonProcess CNPROG
+ WSGIProcessGroup CNPROG
+
+ #force all content to be served as static files
+ #otherwise django will be crunching images through itself wasting time
+ Alias /content/ /path/to/cnprog/templates/content/
+ AliasMatch /([^/]*\.css) /path/to/cnprog/templates/content/style/$1
+
+ Order deny,allow
+ Allow from all
+
+
+ #this is your wsgi script described in the prev section
+ WSGIScriptAlias / /path/to/cnprog/cnprog.wsgi
+
+ #this will force admin interface to work only
+ #through https (optional)
+ #"nimda" is the secret spelling of "admin" ;)
+
+ RewriteEngine on
+ RewriteRule /nimda(.*)$ https://example.com/nimda$1 [L,R=301]
+
+ CustomLog /var/log/httpd/CNPROG/access_log common
+ ErrorLog /var/log/httpd/CNPROG/error_log
+
+#(optional) run admin interface under https
+
+ ServerAdmin forum@example.com
+ DocumentRoot /path/to/cnrpog
+ ServerName example.com
+ SSLEngine on
+ SSLCertificateFile /path/to/ssl-certificate/server.crt
+ SSLCertificateKeyFile /path/to/ssl-certificate/server.key
+ WSGIScriptAlias / /path/to/cnprogcnprog.wsgi
+ CustomLog /var/log/httpd/CNPROG/access_log common
+ ErrorLog /var/log/httpd/CNPROG/error_log
+ DirectoryIndex index.html
+
+-------------
+
+5. Full text search (using sphinx search)
+ Currently full text search works only with sphinx search engine
+ Sphinx at this time supports only MySQL and PostgreSQL databases
+ to enable this, install sphinx search engine and djangosphinx
+
+ configure sphinx, sample configuration can be found in
+ sphinx/sphinx.conf file usually goes somewhere in /etc tree
+
+ build cnprog index first time manually
+
+ % indexer --config /path/to/sphinx.conf --index cnprog
+
+ setup cron job to rebuild index periodically with command
+ your crontab entry may be something like
+
+ 0 9,15,21 * * * /usr/local/bin/indexer --config /etc/sphinx/sphinx.conf --all --rotate >/dev/null 2>&1
+ adjust it as necessary this one will reindex three times a day at 9am 3pm and 9pm
+
+ if your forum grows very big ( good luck with that :) you'll
+ need to two search indices one diff index and one main
+ please refer to online sphinx search documentation for the information
+ on the subject http://sphinxsearch.com/docs/
+
+ in settings_local.py set
+ USE_SPHINX_SEARCH=True
+ adjust other settings that have SPHINX_* prefix accordingly
+ remember that there must be trailing comma in parentheses for
+ SHPINX_SEARCH_INDICES tuple - particlarly with just one item!
+
+ in settings.py look for INSTALLED_APPS
+ and uncomment #'djangosphinx',
+
+
+6. Email subscriptions
+
+ This function at the moment requires Django 1.1
+
+ edit paths in the file cron/send_email_alerts
+ set up a cron job to call cron/send_email_alerts once or twice a day
+ subscription sender may be tested manually in shell
+ by calling cron/send_email_alerts
+
+7. Sitemap
+Sitemap will be available at /sitemap.xml
+e.g yoursite.com/forum/sitemap.xml
+
+google will be pinged each time question, answer or
+comment is saved or a question deleted
+
+for this to be useful - do register you sitemap with Google at
+https://www.google.com/webmasters/tools/
+
+8. Miscellaneous
+
+There are some demo scripts under sql_scripts folder,
+including badges and test accounts for CNProg.com. You
+don't need them to run your sample.
+
+C. CONFIGURATION PARAMETERS
+
+#the only parameter that needs to be touched in settings.py is
+DEBUG=False #set to True to enable debug mode
+
+#all forum parameters are set in file settings_local.py
+
+LOG_FILENAME = 'cnprog.log' #where logging messages should go
+DATABASE_NAME = 'cnprog' # Or path to database file if using sqlite3.
+DATABASE_USER = '' # Not used with sqlite3.
+DATABASE_PASSWORD = '' # Not used with sqlite3.
+DATABASE_ENGINE = 'mysql' #mysql, etc
+SERVER_EMAIL = ''
+DEFAULT_FROM_EMAIL = ''
+EMAIL_HOST_USER = ''
+EMAIL_HOST_PASSWORD = '' #not necessary if mailserver is run on local machine
+EMAIL_SUBJECT_PREFIX = '[CNPROG] '
+EMAIL_HOST='cnprog.com'
+EMAIL_PORT='25'
+EMAIL_USE_TLS=False
+TIME_ZONE = 'America/Tijuana'
+APP_TITLE = u'CNPROG Q&A Forum' #title of your forum
+APP_KEYWORDS = u'CNPROG,forum,community' #keywords for search engines
+APP_DESCRIPTION = u'Ask and answer questions.' #site description for searche engines
+APP_INTRO = u'
Ask and answer questions, make the world better!
' #slogan that goes to front page in logged out mode
+APP_COPYRIGHT = '' #copyright message
+
+#if you set FORUM_SCRIPT_ALIAS= 'forum/'
+#then CNPROG will run at url http://example.com/forum
+#FORUM_SCRIPT_ALIAS cannot have leading slash, otherwise it can be set to anything
+FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string
+
+LANGUAGE_CODE = 'en' #forum language (see language instructions on the wiki)
+EMAIL_VALIDATION = 'off' #string - on|off
+MIN_USERNAME_LENGTH = 1
+EMAIL_UNIQUE = False #if True, email addresses must be unique in all accounts
+APP_URL = 'http://cnprog.com' #used by email notif system and RSS
+GOOGLE_SITEMAP_CODE = '' #code for google site crawler (look up google webmaster tools)
+GOOGLE_ANALYTICS_KEY = '' #key to enable google analytics on this site
+BOOKS_ON = False #if True - books tab will be on
+WIKI_ON = True #if False - community wiki feature is disabled
+
+#experimental - allow password login through external site
+#must implement django_authopenid/external_login.py
+#included prototype external_login works with Mediawiki
+USE_EXTERNAL_LEGACY_LOGIN = True #if false CNPROG uses it's own login/password
+EXTERNAL_LEGACY_LOGIN_HOST = 'login.cnprog.com'
+EXTERNAL_LEGACY_LOGIN_PORT = 80
+EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'CNPROG'
+
+FEEDBACK_SITE_URL = None #None or url
+LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
+
+DJANGO_VERSION = 1.1 #must be either 1.0 or 1.1
+RESOURCE_REVISION=4 #increment when you update media files - clients will be forced to load new version
+
+D. Customization
+
+Other than settings_local.py the following will most likely need customization:
+* locale/*/django.po - language files that may also contain your site-specific messages
+ if you want to start with english messages file - look for words like "forum" and
+ "CNPROG" in the msgstr lines
+* templates/header.html and templates/footer.html may contain extra links
+* templates/about.html - a place to explain for is your forum for
+* templates/faq.html - put answers to users frequent questions
+* templates/content/style/style.css - modify style sheet to add disctinctive look to your forum
diff --git a/settings.py b/settings.py
index 6310ce41..5cc06b78 100644
--- a/settings.py
+++ b/settings.py
@@ -1,71 +1,71 @@
-# encoding:utf-8
-# Django settings for lanai project.
-import os.path
-import sys
-
-SITE_ID = 1
-
-ADMIN_MEDIA_PREFIX = '/forum/admin/media/'
-SECRET_KEY = '$oo^&_m&qwbib=(_4m_n*zn-d=g#s0he5fx9xonnym#8p6yigm'
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.load_template_source',
- 'django.template.loaders.app_directories.load_template_source',
-# 'django.template.loaders.eggs.load_template_source',
-)
-
-MIDDLEWARE_CLASSES = (
- 'django.middleware.gzip.GZipMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- #'django.middleware.locale.LocaleMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.middleware.transaction.TransactionMiddleware',
- #'django.middleware.sqlprint.SqlPrintingMiddleware',
- 'middleware.anon_user.ConnectToSessionMessagesMiddleware',
- 'middleware.pagesize.QuestionsPageSizeMiddleware',
- 'middleware.cancel.CancelActionMiddleware',
- 'debug_toolbar.middleware.DebugToolbarMiddleware',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
- 'django.core.context_processors.request',
- 'context.application_settings',
- #'django.core.context_processors.i18n',
- 'user_messages.context_processors.user_messages',#must be before auth
- 'django.core.context_processors.auth', #this is required for admin
-)
-
-ROOT_URLCONF = 'urls'
-
-TEMPLATE_DIRS = (
- os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
-)
-
-#UPLOAD SETTINGS
-FILE_UPLOAD_TEMP_DIR = os.path.join(os.path.dirname(__file__), 'tmp').replace('\\','/')
-FILE_UPLOAD_HANDLERS = ("django.core.files.uploadhandler.MemoryFileUploadHandler",
- "django.core.files.uploadhandler.TemporaryFileUploadHandler",)
-DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
-# for user upload
-ALLOW_FILE_TYPES = ('.jpg', '.jpeg', '.gif', '.bmp', '.png', '.tiff')
-# unit byte
-ALLOW_MAX_FILE_SIZE = 1024 * 1024
-
-INSTALLED_APPS = (
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.sites',
- 'django.contrib.admin',
- 'django.contrib.humanize',
- 'django.contrib.sitemaps',
- 'forum',
- 'django_authopenid',
- 'djangosphinx',
- 'debug_toolbar' ,
- 'user_messages',
-)
-
-# User settings
-from settings_local import *
+# encoding:utf-8
+# Django settings for lanai project.
+import os.path
+import sys
+
+SITE_ID = 1
+
+ADMIN_MEDIA_PREFIX = '/forum/admin/media/'
+SECRET_KEY = '$oo^&_m&qwbib=(_4m_n*zn-d=g#s0he5fx9xonnym#8p6yigm'
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.load_template_source',
+ 'django.template.loaders.app_directories.load_template_source',
+# 'django.template.loaders.eggs.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.gzip.GZipMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ #'django.middleware.locale.LocaleMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.middleware.transaction.TransactionMiddleware',
+ #'django.middleware.sqlprint.SqlPrintingMiddleware',
+ 'middleware.anon_user.ConnectToSessionMessagesMiddleware',
+ 'middleware.pagesize.QuestionsPageSizeMiddleware',
+ 'middleware.cancel.CancelActionMiddleware',
+ 'debug_toolbar.middleware.DebugToolbarMiddleware',
+)
+
+TEMPLATE_CONTEXT_PROCESSORS = (
+ 'django.core.context_processors.request',
+ 'context.application_settings',
+ #'django.core.context_processors.i18n',
+ 'user_messages.context_processors.user_messages',#must be before auth
+ 'django.core.context_processors.auth', #this is required for admin
+)
+
+ROOT_URLCONF = 'urls'
+
+TEMPLATE_DIRS = (
+ os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
+)
+
+#UPLOAD SETTINGS
+FILE_UPLOAD_TEMP_DIR = os.path.join(os.path.dirname(__file__), 'tmp').replace('\\','/')
+FILE_UPLOAD_HANDLERS = ("django.core.files.uploadhandler.MemoryFileUploadHandler",
+ "django.core.files.uploadhandler.TemporaryFileUploadHandler",)
+DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
+# for user upload
+ALLOW_FILE_TYPES = ('.jpg', '.jpeg', '.gif', '.bmp', '.png', '.tiff')
+# unit byte
+ALLOW_MAX_FILE_SIZE = 1024 * 1024
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.admin',
+ 'django.contrib.humanize',
+ 'django.contrib.sitemaps',
+ 'forum',
+ 'django_authopenid',
+ #'djangosphinx',
+ 'debug_toolbar' ,
+ 'user_messages',
+)
+
+# User settings
+from settings_local import *
--
cgit v1.2.3-1-g7c22
From e8bfbf711f9681d315767c75591c3d63d055b320 Mon Sep 17 00:00:00 2001
From: hrcerqueira
Date: Wed, 20 Jan 2010 15:36:01 +0000
Subject: Changed os.path.normpath to posixpath.normpath in the href tag
definition so it now works on windows hosts.
---
forum/templatetags/extra_tags.py | 701 ++++++++++++++++++++-------------------
1 file changed, 351 insertions(+), 350 deletions(-)
diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py
index 0694e5d4..938d853f 100644
--- a/forum/templatetags/extra_tags.py
+++ b/forum/templatetags/extra_tags.py
@@ -1,350 +1,351 @@
-import time
-import os
-import datetime
-import math
-import re
-import logging
-from django import template
-from django.utils.encoding import smart_unicode
-from django.utils.safestring import mark_safe
-from forum.const import *
-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
-
-register = template.Library()
-
-GRAVATAR_TEMPLATE = ('')
-
-@register.simple_tag
-def gravatar(user, size):
- """
- Creates an ```` for a user's Gravatar with a given size.
-
- This tag can accept a User object, or a dict containing the
- appropriate values.
- """
- try:
- gravatar = user['gravatar']
- username = user['username']
- except (TypeError, AttributeError, KeyError):
- gravatar = user.gravatar
- username = user.username
- return mark_safe(GRAVATAR_TEMPLATE % {
- 'size': size,
- 'gravatar_hash': gravatar,
- 'username': template.defaultfilters.urlencode(username),
- })
-
-MAX_FONTSIZE = 18
-MIN_FONTSIZE = 12
-@register.simple_tag
-def tag_font_size(max_size, min_size, current_size):
- """
- do a logarithmic mapping calcuation for a proper size for tagging cloud
- Algorithm from http://blogs.dekoh.com/dev/2007/10/29/choosing-a-good-font-size-variation-algorithm-for-your-tag-cloud/
- """
- #avoid invalid calculation
- if current_size == 0:
- current_size = 1
- try:
- weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
- except:
- weight = 0
- return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
-
-
-LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
-LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
-NUM_PAGES_OUTSIDE_RANGE = 1
-ADJACENT_PAGES = 2
-@register.inclusion_tag("paginator.html")
-def cnprog_paginator(context):
- """
- custom paginator tag
- Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
- """
- if (context["is_paginated"]):
- " Initialize variables "
- in_leading_range = in_trailing_range = False
- pages_outside_leading_range = pages_outside_trailing_range = range(0)
-
- if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
- in_leading_range = in_trailing_range = True
- page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
- elif (context["page"] <= LEADING_PAGE_RANGE):
- in_leading_range = True
- page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
- pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
- elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
- in_trailing_range = True
- page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
- pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
- else:
- page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if n > 0 and n <= context["pages"]]
- pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
- pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
-
- extend_url = context.get('extend_url', '')
- return {
- "base_url": context["base_url"],
- "is_paginated": context["is_paginated"],
- "previous": context["previous"],
- "has_previous": context["has_previous"],
- "next": context["next"],
- "has_next": context["has_next"],
- "page": context["page"],
- "pages": context["pages"],
- "page_numbers": page_numbers,
- "in_leading_range" : in_leading_range,
- "in_trailing_range" : in_trailing_range,
- "pages_outside_leading_range": pages_outside_leading_range,
- "pages_outside_trailing_range": pages_outside_trailing_range,
- "extend_url" : extend_url
- }
-
-@register.inclusion_tag("pagesize.html")
-def cnprog_pagesize(context):
- """
- display the pagesize selection boxes for paginator
- """
- if (context["is_paginated"]):
- return {
- "base_url": context["base_url"],
- "pagesize" : context["pagesize"],
- "is_paginated": context["is_paginated"]
- }
-
-@register.inclusion_tag("post_contributor_info.html")
-def post_contributor_info(post,contributor_type='original_author'):
- """contributor_type: original_author|last_updater
- """
- if isinstance(post,Question):
- post_type = 'question'
- elif isinstance(post,Answer):
- post_type = 'answer'
- elif isinstance(post,AnswerRevision) or isinstance(post,QuestionRevision):
- post_type = 'revision'
- return {
- 'post':post,
- 'post_type':post_type,
- 'wiki_on':settings.WIKI_ON,
- 'contributor_type':contributor_type
- }
-
-@register.simple_tag
-def get_score_badge(user):
- BADGE_TEMPLATE = '%(reputation)s'
- if user.gold > 0 :
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
- '●'
- '%(gold)s'
- '')
- if user.silver > 0:
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
- '●'
- '%(silver)s'
- '')
- if user.bronze > 0:
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
- '●'
- '%(bronze)s'
- '')
- BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
- return mark_safe(BADGE_TEMPLATE % {
- 'reputation' : user.reputation,
- 'gold' : user.gold,
- 'silver' : user.silver,
- 'bronze' : user.bronze,
- 'badgesword' : _('badges'),
- 'reputationword' : _('reputation points'),
- })
-
-@register.simple_tag
-def get_score_badge_by_details(rep, gold, silver, bronze):
- BADGE_TEMPLATE = '%(reputation)s'
- if gold > 0 :
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
- '●'
- '%(gold)s'
- '')
- if silver > 0:
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
- '●'
- '%(silver)s'
- '')
- if bronze > 0:
- BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
- '●'
- '%(bronze)s'
- '')
- BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
- return mark_safe(BADGE_TEMPLATE % {
- 'reputation' : rep,
- 'gold' : gold,
- 'silver' : silver,
- 'bronze' : bronze,
- 'repword' : _('reputation points'),
- 'badgeword' : _('badges'),
- })
-
-@register.simple_tag
-def get_user_vote_image(dic, key, arrow):
- if dic.has_key(key):
- if int(dic[key]) == int(arrow):
- return '-on'
- return ''
-
-@register.simple_tag
-def get_age(birthday):
- current_time = datetime.datetime(*time.localtime()[0:6])
- year = birthday.year
- month = birthday.month
- day = birthday.day
- diff = current_time - datetime.datetime(year,month,day,0,0,0)
- return diff.days / 365
-
-@register.simple_tag
-def get_total_count(up_count, down_count):
- return up_count + down_count
-
-@register.simple_tag
-def format_number(value):
- strValue = str(value)
- if len(strValue) <= 3:
- return strValue
- result = ''
- first = ''
- pattern = re.compile('(-?\d+)(\d{3})')
- m = re.match(pattern, strValue)
- while m != None:
- first = m.group(1)
- second = m.group(2)
- result = ',' + second + result
- strValue = first + ',' + second
- m = re.match(pattern, strValue)
- return first + result
-
-@register.simple_tag
-def convert2tagname_list(question):
- question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
- return ''
-
-@register.simple_tag
-def diff_date(date, limen=2):
- now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
- diff = now - date
- days = diff.days
- hours = int(diff.seconds/3600)
- minutes = int(diff.seconds/60)
-
- if days > 2:
- if date.year == now.year:
- return date.strftime("%b %d at %H:%M")
- else:
- return date.strftime("%b %d '%y at %H:%M")
- elif days == 2:
- return _('2 days ago')
- elif days == 1:
- return _('yesterday')
- elif minutes >= 60:
- return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
- else:
- return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
-
-@register.simple_tag
-def get_latest_changed_timestamp():
- try:
- from time import localtime, strftime
- from os import path
- root = settings.SITE_SRC_ROOT
- dir = (
- root,
- '%s/forum' % root,
- '%s/templates' % root,
- )
- stamp = (path.getmtime(d) for d in dir)
- latest = max(stamp)
- timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
- except:
- timestr = ''
- return timestr
-
-@register.simple_tag
-def href(url):
- url = '///' + settings.FORUM_SCRIPT_ALIAS + '/' + url
- return os.path.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
-
-class ItemSeparatorNode(template.Node):
- def __init__(self,separator):
- sep = separator.strip()
- if sep[0] == sep[-1] and sep[0] in ('\'','"'):
- sep = sep[1:-1]
- else:
- raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
- self.content = sep
- def render(self,context):
- return self.content
-
-class JoinItemListNode(template.Node):
- def __init__(self,separator=ItemSeparatorNode("''"), items=()):
- self.separator = separator
- self.items = items
- def render(self,context):
- out = []
- empty_re = re.compile(r'^\s*$')
- for item in self.items:
- bit = item.render(context)
- if not empty_re.search(bit):
- out.append(bit)
- return self.separator.render(context).join(out)
-
-@register.tag(name="joinitems")
-def joinitems(parser,token):
- try:
- tagname,junk,sep_token = token.split_contents()
- except ValueError:
- raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
- if junk == 'using':
- sep_node = ItemSeparatorNode(sep_token)
- else:
- raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
- nodelist = []
- while True:
- nodelist.append(parser.parse(('separator','endjoinitems')))
- next = parser.next_token()
- if next.contents == 'endjoinitems':
- break
-
- return JoinItemListNode(separator=sep_node,items=nodelist)
-
-class BlockResourceNode(template.Node):
- def __init__(self,nodelist):
- self.items = nodelist
- def render(self,context):
- out = '///' + settings.FORUM_SCRIPT_ALIAS
- if self.items:
- out += '/'
- for item in self.items:
- bit = item.render(context)
- out += bit
- out = os.path.normpath(out) + '?v=%d' % settings.RESOURCE_REVISION
- return out.replace(' ','')
-
-@register.tag(name='blockresource')
-def blockresource(parser,token):
- try:
- tagname = token.split_contents()
- except ValueError:
- raise template.TemplateSyntaxError("blockresource tag does not use arguments")
- nodelist = []
- while True:
- nodelist.append(parser.parse(('endblockresource')))
- next = parser.next_token()
- if next.contents == 'endblockresource':
- break
- return BlockResourceNode(nodelist)
+import time
+import os
+import posixpath
+import datetime
+import math
+import re
+import logging
+from django import template
+from django.utils.encoding import smart_unicode
+from django.utils.safestring import mark_safe
+from forum.const import *
+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
+
+register = template.Library()
+
+GRAVATAR_TEMPLATE = ('')
+
+@register.simple_tag
+def gravatar(user, size):
+ """
+ Creates an ```` for a user's Gravatar with a given size.
+
+ This tag can accept a User object, or a dict containing the
+ appropriate values.
+ """
+ try:
+ gravatar = user['gravatar']
+ username = user['username']
+ except (TypeError, AttributeError, KeyError):
+ gravatar = user.gravatar
+ username = user.username
+ return mark_safe(GRAVATAR_TEMPLATE % {
+ 'size': size,
+ 'gravatar_hash': gravatar,
+ 'username': template.defaultfilters.urlencode(username),
+ })
+
+MAX_FONTSIZE = 18
+MIN_FONTSIZE = 12
+@register.simple_tag
+def tag_font_size(max_size, min_size, current_size):
+ """
+ do a logarithmic mapping calcuation for a proper size for tagging cloud
+ Algorithm from http://blogs.dekoh.com/dev/2007/10/29/choosing-a-good-font-size-variation-algorithm-for-your-tag-cloud/
+ """
+ #avoid invalid calculation
+ if current_size == 0:
+ current_size = 1
+ try:
+ weight = (math.log10(current_size) - math.log10(min_size)) / (math.log10(max_size) - math.log10(min_size))
+ except:
+ weight = 0
+ return MIN_FONTSIZE + round((MAX_FONTSIZE - MIN_FONTSIZE) * weight)
+
+
+LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
+LEADING_PAGE_RANGE = TRAILING_PAGE_RANGE = 4
+NUM_PAGES_OUTSIDE_RANGE = 1
+ADJACENT_PAGES = 2
+@register.inclusion_tag("paginator.html")
+def cnprog_paginator(context):
+ """
+ custom paginator tag
+ Inspired from http://blog.localkinegrinds.com/2007/09/06/digg-style-pagination-in-django/
+ """
+ if (context["is_paginated"]):
+ " Initialize variables "
+ in_leading_range = in_trailing_range = False
+ pages_outside_leading_range = pages_outside_trailing_range = range(0)
+
+ if (context["pages"] <= LEADING_PAGE_RANGE_DISPLAYED):
+ in_leading_range = in_trailing_range = True
+ page_numbers = [n for n in range(1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
+ elif (context["page"] <= LEADING_PAGE_RANGE):
+ in_leading_range = True
+ page_numbers = [n for n in range(1, LEADING_PAGE_RANGE_DISPLAYED + 1) if n > 0 and n <= context["pages"]]
+ pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
+ elif (context["page"] > context["pages"] - TRAILING_PAGE_RANGE):
+ in_trailing_range = True
+ page_numbers = [n for n in range(context["pages"] - TRAILING_PAGE_RANGE_DISPLAYED + 1, context["pages"] + 1) if n > 0 and n <= context["pages"]]
+ pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
+ else:
+ page_numbers = [n for n in range(context["page"] - ADJACENT_PAGES, context["page"] + ADJACENT_PAGES + 1) if n > 0 and n <= context["pages"]]
+ pages_outside_leading_range = [n + context["pages"] for n in range(0, -NUM_PAGES_OUTSIDE_RANGE, -1)]
+ pages_outside_trailing_range = [n + 1 for n in range(0, NUM_PAGES_OUTSIDE_RANGE)]
+
+ extend_url = context.get('extend_url', '')
+ return {
+ "base_url": context["base_url"],
+ "is_paginated": context["is_paginated"],
+ "previous": context["previous"],
+ "has_previous": context["has_previous"],
+ "next": context["next"],
+ "has_next": context["has_next"],
+ "page": context["page"],
+ "pages": context["pages"],
+ "page_numbers": page_numbers,
+ "in_leading_range" : in_leading_range,
+ "in_trailing_range" : in_trailing_range,
+ "pages_outside_leading_range": pages_outside_leading_range,
+ "pages_outside_trailing_range": pages_outside_trailing_range,
+ "extend_url" : extend_url
+ }
+
+@register.inclusion_tag("pagesize.html")
+def cnprog_pagesize(context):
+ """
+ display the pagesize selection boxes for paginator
+ """
+ if (context["is_paginated"]):
+ return {
+ "base_url": context["base_url"],
+ "pagesize" : context["pagesize"],
+ "is_paginated": context["is_paginated"]
+ }
+
+@register.inclusion_tag("post_contributor_info.html")
+def post_contributor_info(post,contributor_type='original_author'):
+ """contributor_type: original_author|last_updater
+ """
+ if isinstance(post,Question):
+ post_type = 'question'
+ elif isinstance(post,Answer):
+ post_type = 'answer'
+ elif isinstance(post,AnswerRevision) or isinstance(post,QuestionRevision):
+ post_type = 'revision'
+ return {
+ 'post':post,
+ 'post_type':post_type,
+ 'wiki_on':settings.WIKI_ON,
+ 'contributor_type':contributor_type
+ }
+
+@register.simple_tag
+def get_score_badge(user):
+ BADGE_TEMPLATE = '%(reputation)s'
+ if user.gold > 0 :
+ BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
+ '●'
+ '%(gold)s'
+ '')
+ if user.silver > 0:
+ BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
+ '●'
+ '%(silver)s'
+ '')
+ if user.bronze > 0:
+ BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
+ '●'
+ '%(bronze)s'
+ '')
+ BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
+ return mark_safe(BADGE_TEMPLATE % {
+ 'reputation' : user.reputation,
+ 'gold' : user.gold,
+ 'silver' : user.silver,
+ 'bronze' : user.bronze,
+ 'badgesword' : _('badges'),
+ 'reputationword' : _('reputation points'),
+ })
+
+@register.simple_tag
+def get_score_badge_by_details(rep, gold, silver, bronze):
+ BADGE_TEMPLATE = '%(reputation)s'
+ if gold > 0 :
+ BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
+ '●'
+ '%(gold)s'
+ '')
+ if silver > 0:
+ BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
+ '●'
+ '%(silver)s'
+ '')
+ if bronze > 0:
+ BADGE_TEMPLATE = '%s%s' % (BADGE_TEMPLATE, ''
+ '●'
+ '%(bronze)s'
+ '')
+ BADGE_TEMPLATE = smart_unicode(BADGE_TEMPLATE, encoding='utf-8', strings_only=False, errors='strict')
+ return mark_safe(BADGE_TEMPLATE % {
+ 'reputation' : rep,
+ 'gold' : gold,
+ 'silver' : silver,
+ 'bronze' : bronze,
+ 'repword' : _('reputation points'),
+ 'badgeword' : _('badges'),
+ })
+
+@register.simple_tag
+def get_user_vote_image(dic, key, arrow):
+ if dic.has_key(key):
+ if int(dic[key]) == int(arrow):
+ return '-on'
+ return ''
+
+@register.simple_tag
+def get_age(birthday):
+ current_time = datetime.datetime(*time.localtime()[0:6])
+ year = birthday.year
+ month = birthday.month
+ day = birthday.day
+ diff = current_time - datetime.datetime(year,month,day,0,0,0)
+ return diff.days / 365
+
+@register.simple_tag
+def get_total_count(up_count, down_count):
+ return up_count + down_count
+
+@register.simple_tag
+def format_number(value):
+ strValue = str(value)
+ if len(strValue) <= 3:
+ return strValue
+ result = ''
+ first = ''
+ pattern = re.compile('(-?\d+)(\d{3})')
+ m = re.match(pattern, strValue)
+ while m != None:
+ first = m.group(1)
+ second = m.group(2)
+ result = ',' + second + result
+ strValue = first + ',' + second
+ m = re.match(pattern, strValue)
+ return first + result
+
+@register.simple_tag
+def convert2tagname_list(question):
+ question['tagnames'] = [name for name in question['tagnames'].split(u' ')]
+ return ''
+
+@register.simple_tag
+def diff_date(date, limen=2):
+ now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
+ diff = now - date
+ days = diff.days
+ hours = int(diff.seconds/3600)
+ minutes = int(diff.seconds/60)
+
+ if days > 2:
+ if date.year == now.year:
+ return date.strftime("%b %d at %H:%M")
+ else:
+ return date.strftime("%b %d '%y at %H:%M")
+ elif days == 2:
+ return _('2 days ago')
+ elif days == 1:
+ return _('yesterday')
+ elif minutes >= 60:
+ return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
+ else:
+ return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
+
+@register.simple_tag
+def get_latest_changed_timestamp():
+ try:
+ from time import localtime, strftime
+ from os import path
+ root = settings.SITE_SRC_ROOT
+ dir = (
+ root,
+ '%s/forum' % root,
+ '%s/templates' % root,
+ )
+ stamp = (path.getmtime(d) for d in dir)
+ latest = max(stamp)
+ timestr = strftime("%H:%M %b-%d-%Y %Z", localtime(latest))
+ except:
+ timestr = ''
+ return timestr
+
+@register.simple_tag
+def href(url):
+ url = '///' + settings.FORUM_SCRIPT_ALIAS + '/' + url
+ return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
+
+class ItemSeparatorNode(template.Node):
+ def __init__(self,separator):
+ sep = separator.strip()
+ if sep[0] == sep[-1] and sep[0] in ('\'','"'):
+ sep = sep[1:-1]
+ else:
+ raise template.TemplateSyntaxError('separator in joinitems tag must be quoted')
+ self.content = sep
+ def render(self,context):
+ return self.content
+
+class JoinItemListNode(template.Node):
+ def __init__(self,separator=ItemSeparatorNode("''"), items=()):
+ self.separator = separator
+ self.items = items
+ def render(self,context):
+ out = []
+ empty_re = re.compile(r'^\s*$')
+ for item in self.items:
+ bit = item.render(context)
+ if not empty_re.search(bit):
+ out.append(bit)
+ return self.separator.render(context).join(out)
+
+@register.tag(name="joinitems")
+def joinitems(parser,token):
+ try:
+ tagname,junk,sep_token = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
+ if junk == 'using':
+ sep_node = ItemSeparatorNode(sep_token)
+ else:
+ raise template.TemplateSyntaxError("joinitems tag requires 'using \"separator html\"' parameters")
+ nodelist = []
+ while True:
+ nodelist.append(parser.parse(('separator','endjoinitems')))
+ next = parser.next_token()
+ if next.contents == 'endjoinitems':
+ break
+
+ return JoinItemListNode(separator=sep_node,items=nodelist)
+
+class BlockResourceNode(template.Node):
+ def __init__(self,nodelist):
+ self.items = nodelist
+ def render(self,context):
+ out = '///' + settings.FORUM_SCRIPT_ALIAS
+ if self.items:
+ out += '/'
+ for item in self.items:
+ bit = item.render(context)
+ out += bit
+ out = os.path.normpath(out) + '?v=%d' % settings.RESOURCE_REVISION
+ return out.replace(' ','')
+
+@register.tag(name='blockresource')
+def blockresource(parser,token):
+ try:
+ tagname = token.split_contents()
+ except ValueError:
+ raise template.TemplateSyntaxError("blockresource tag does not use arguments")
+ nodelist = []
+ while True:
+ nodelist.append(parser.parse(('endblockresource')))
+ next = parser.next_token()
+ if next.contents == 'endblockresource':
+ break
+ return BlockResourceNode(nodelist)
--
cgit v1.2.3-1-g7c22
From 7518716209e49ecd76ea4de1f169216f8f4fa268 Mon Sep 17 00:00:00 2001
From: hrcerqueira
Date: Wed, 20 Jan 2010 16:43:55 +0000
Subject: Fixed the redirect when anonymous users try to vote.
---
templates/content/js/com.cnprog.post.js | 1377 ++++++++++++++++---------------
templates/question.html | 1020 +++++++++++------------
2 files changed, 1200 insertions(+), 1197 deletions(-)
diff --git a/templates/content/js/com.cnprog.post.js b/templates/content/js/com.cnprog.post.js
index 33df1e21..02ed7757 100644
--- a/templates/content/js/com.cnprog.post.js
+++ b/templates/content/js/com.cnprog.post.js
@@ -1,687 +1,690 @@
-/*
-Scripts for cnprog.com
-Project Name: Lanai
-All Rights Resevred 2008. CNPROG.COM
-*/
-var lanai =
-{
- /**
- * Finds any
{% blocktrans with question.get_close_reason_display as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %}
- {{ question.closed_by.username }}
- {% blocktrans with question.closed_at as closed_at %}close date {{closed_at}}{% endblocktrans %}
{% blocktrans with question.get_close_reason_display as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %}
+ {{ question.closed_by.username }}
+ {% blocktrans with question.closed_at as closed_at %}close date {{closed_at}}{% endblocktrans %}
- {% if searchtag %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num and searchtag as tagname %}
- have total {{q_num}} questions tagged {{tagname}}
- {% plural %}
- have total {{q_num}} questions tagged {{tagname}}
- {% endblocktrans %}
- {% else %}
- {% if searchtitle %}
- {% if settings.USE_SPHINX_SEARCH %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions containing {{searchtitle}} in full text
- {% plural %}
- have total {{q_num}} questions containing {{searchtitle}} in full text
- {% endblocktrans %}
- {% else %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions containing {{searchtitle}}
- {% plural %}
- have total {{q_num}} questions containing {{searchtitle}}
- {% endblocktrans %}
- {% endif %}
- {% else %}
- {% if is_unanswered %}
- {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} unanswered questions
- {% plural %}
- have total {{q_num}} unanswered questions
- {% endblocktrans %}
- {% else %}
- {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions
- {% plural %}
- have total {{q_num}} questions
- {% endblocktrans %}
- {% endif %}
- {% endif %}
- {% endif %}
-
- {% ifequal tab_id "latest" %}
- {% trans "latest questions info" %}
- {% endifequal %}
-
- {% ifequal tab_id "active" %}
- {% trans "Questions are sorted by the time of last update." %}
- {% trans "Most recently answered ones are shown first." %}
- {% endifequal %}
-
- {% ifequal tab_id "hottest" %}
- {% trans "Questions sorted by number of responses." %}
- {% trans "Most answered questions are shown first." %}
- {% endifequal %}
-
- {% ifequal tab_id "mostvoted" %}
- {% trans "Questions are sorted by the number of votes." %}
- {% trans "Most voted questions are shown first." %}
- {% endifequal %}
-
-
-{% if request.user.is_authenticated %}
-{% include "tag_selector.html" %}
-{% endif %}
-
-
{% trans "Related tags" %}
-
- {% for tag in tags %}
- {{ tag.name }}
- × {{ tag.used_count|intcomma }}
-
- {% endfor %}
-
+ {% if searchtag %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num and searchtag as tagname %}
+ have total {{q_num}} questions tagged {{tagname}}
+ {% plural %}
+ have total {{q_num}} questions tagged {{tagname}}
+ {% endblocktrans %}
+ {% else %}
+ {% if searchtitle %}
+ {% if settings.USE_SPHINX_SEARCH %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions containing {{searchtitle}} in full text
+ {% plural %}
+ have total {{q_num}} questions containing {{searchtitle}} in full text
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions containing {{searchtitle}}
+ {% plural %}
+ have total {{q_num}} questions containing {{searchtitle}}
+ {% endblocktrans %}
+ {% endif %}
+ {% else %}
+ {% if is_unanswered %}
+ {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} unanswered questions
+ {% plural %}
+ have total {{q_num}} unanswered questions
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions
+ {% plural %}
+ have total {{q_num}} questions
+ {% endblocktrans %}
+ {% endif %}
+ {% endif %}
+ {% endif %}
+
+ {% ifequal tab_id "latest" %}
+ {% trans "latest questions info" %}
+ {% endifequal %}
+
+ {% ifequal tab_id "active" %}
+ {% trans "Questions are sorted by the time of last update." %}
+ {% trans "Most recently answered ones are shown first." %}
+ {% endifequal %}
+
+ {% ifequal tab_id "hottest" %}
+ {% trans "Questions sorted by number of responses." %}
+ {% trans "Most answered questions are shown first." %}
+ {% endifequal %}
+
+ {% ifequal tab_id "mostvoted" %}
+ {% trans "Questions are sorted by the number of votes." %}
+ {% trans "Most voted questions are shown first." %}
+ {% endifequal %}
+
+
+{% if request.user.is_authenticated %}
+{% include "tag_selector.html" %}
+{% endif %}
+
+
{% trans "Related tags" %}
+
+ {% for tag in tags %}
+ {{ tag.name }}
+ × {{ tag.used_count|intcomma }}
+
+ {% endfor %}
+
+
+
+{% endblock %}
+
--
cgit v1.2.3-1-g7c22
From 9265259c2d4ae064254b84dae36da095e8cb4c7b Mon Sep 17 00:00:00 2001
From: hrcerqueira
Date: Wed, 20 Jan 2010 19:37:10 +0000
Subject: Simple fix on an sql querie to make it db agnostic.
---
forum/managers.py | 481 +++++++++++++++++++++++++++---------------------------
1 file changed, 241 insertions(+), 240 deletions(-)
diff --git a/forum/managers.py b/forum/managers.py
index 90437e91..ce67c237 100644
--- a/forum/managers.py
+++ b/forum/managers.py
@@ -1,240 +1,241 @@
-import datetime
-import logging
-from django.contrib.auth.models import User, UserManager
-from django.db import connection, models, transaction
-from django.db.models import Q
-from forum.models import *
-from urllib import quote, unquote
-
-class QuestionManager(models.Manager):
-
- def update_tags(self, question, tagnames, user):
- """
- Updates Tag associations for a question to match the given
- tagname string.
-
- Returns ``True`` if tag usage counts were updated as a result,
- ``False`` otherwise.
- """
- from forum.models import Tag
- current_tags = list(question.tags.all())
- current_tagnames = set(t.name for t in current_tags)
- updated_tagnames = set(t for t in tagnames.split(' ') if t)
- modified_tags = []
-
- removed_tags = [t for t in current_tags
- if t.name not in updated_tagnames]
- if removed_tags:
- modified_tags.extend(removed_tags)
- question.tags.remove(*removed_tags)
-
- added_tagnames = updated_tagnames - current_tagnames
- if added_tagnames:
- added_tags = Tag.objects.get_or_create_multiple(added_tagnames,
- user)
- modified_tags.extend(added_tags)
- question.tags.add(*added_tags)
-
- if modified_tags:
- Tag.objects.update_use_counts(modified_tags)
- return True
-
- return False
-
- def update_answer_count(self, question):
- """
- Executes an UPDATE query to update denormalised data with the
- number of answers the given question has.
- """
-
- # for some reasons, this Answer class failed to be imported,
- # although we have imported all classes from models on top.
- from forum.models import Answer
- self.filter(id=question.id).update(
- answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count())
-
- def update_view_count(self, question):
- """
- update counter+1 when user browse question page
- """
- self.filter(id=question.id).update(view_count = question.view_count + 1)
-
- def update_favorite_count(self, question):
- """
- update favourite_count for given question
- """
- from forum.models import FavoriteQuestion
- self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count())
-
- def get_similar_questions(self, question):
- """
- Get 10 similar questions for given one.
- This will search the same tag list for give question(by exactly same string) first.
- 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)
-
- #print datetime.datetime.now()
- return questions
-
-class TagManager(models.Manager):
- UPDATE_USED_COUNTS_QUERY = (
- 'UPDATE tag '
- 'SET used_count = ('
- 'SELECT COUNT(*) FROM question_tags '
- 'INNER JOIN question ON question_id=question.id '
- 'WHERE tag_id = tag.id AND question.deleted=0'
- ') '
- 'WHERE id IN (%s)')
-
- def get_valid_tags(self, page_size):
- from forum.models import Tag
- tags = Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size]
- return tags
-
- def get_or_create_multiple(self, names, user):
- """
- Fetches a list of Tags with the given names, creating any Tags
- which don't exist when necesssary.
- """
- tags = list(self.filter(name__in=names))
- #Set all these tag visible
- for tag in tags:
- if tag.deleted:
- tag.deleted = False
- tag.deleted_by = None
- tag.deleted_at = None
- tag.save()
-
- if len(tags) < len(names):
- existing_names = set(tag.name for tag in tags)
- new_names = [name for name in names if name not in existing_names]
- tags.extend([self.create(name=name, created_by=user)
- for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0])
-
- return tags
-
- def update_use_counts(self, tags):
- """Updates the given Tags with their current use counts."""
- if not tags:
- return
- cursor = connection.cursor()
- query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags))
- cursor.execute(query, [tag.id for tag in tags])
- transaction.commit_unless_managed()
-
- def get_tags_by_questions(self, questions):
- question_ids = []
- for question in questions:
- question_ids.append(question.id)
-
- question_ids_str = ','.join([str(id) for id in question_ids])
- related_tags = self.extra(
- tables=['tag', 'question_tags'],
- where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"]
- ).distinct()
-
- return related_tags
-
-class AnswerManager(models.Manager):
- GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s'
- def get_answers_from_question(self, question, user=None):
- """
- Retrieves visibile answers for the given question. Delete answers
- are only visibile to the person who deleted them.
- """
-
- if user is None or not user.is_authenticated():
- return self.filter(question=question, deleted=False)
- else:
- return self.filter(Q(question=question),
- Q(deleted=False) | Q(deleted_by=user))
-
- def get_answers_from_questions(self, user_id):
- """
- Retrieves visibile answers for the given question. Which are not included own answers
- """
- cursor = connection.cursor()
- cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id])
- return cursor.fetchall()
-
-class VoteManager(models.Manager):
- COUNT_UP_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = 1"
- COUNT_DOWN_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = -1"
- COUNT_VOTES_PER_DAY_BY_USER = "SELECT COUNT(*) FROM vote WHERE user_id = %s AND DATE(voted_at) = DATE(NOW())"
- def get_up_vote_count_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_UP_VOTE_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
- else:
- return 0
-
- def get_down_vote_count_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_DOWN_VOTE_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
- else:
- return 0
-
- def get_votes_count_today_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_VOTES_PER_DAY_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-
-class FlaggedItemManager(models.Manager):
- COUNT_FLAGS_PER_DAY_BY_USER = "SELECT COUNT(*) FROM flagged_item WHERE user_id = %s AND DATE(flagged_at) = DATE(NOW())"
- def get_flagged_items_count_today(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_FLAGS_PER_DAY_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-
-class ReputeManager(models.Manager):
- COUNT_REPUTATION_PER_DAY_BY_USER = "SELECT SUM(positive)+SUM(negative) FROM repute WHERE user_id = %s AND (reputation_type=1 OR reputation_type=-8) AND DATE(reputed_at) = DATE(NOW())"
- def get_reputation_by_upvoted_today(self, user):
- """
- For one user in one day, he can only earn rep till certain score (ep. +200)
- by upvoted(also substracted from upvoted canceled). This is because we need
- to prohibit gaming system by upvoting/cancel again and again.
- """
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_REPUTATION_PER_DAY_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-class AwardManager(models.Manager):
- def get_recent_awards(self):
- awards = super(AwardManager, self).extra(
- select={'badge_id': 'badge.id', 'badge_name':'badge.name',
- 'badge_description': 'badge.description', 'badge_type': 'badge.type',
- 'user_id': 'auth_user.id', 'user_name': 'auth_user.username'
- },
- tables=['award', 'badge', 'auth_user'],
- order_by=['-awarded_at'],
- where=['auth_user.id=award.user_id AND badge_id=badge.id'],
- ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name')
- return awards
+import datetime
+import time
+import logging
+from django.contrib.auth.models import User, UserManager
+from django.db import connection, models, transaction
+from django.db.models import Q
+from forum.models import *
+from urllib import quote, unquote
+
+class QuestionManager(models.Manager):
+
+ def update_tags(self, question, tagnames, user):
+ """
+ Updates Tag associations for a question to match the given
+ tagname string.
+
+ Returns ``True`` if tag usage counts were updated as a result,
+ ``False`` otherwise.
+ """
+ from forum.models import Tag
+ current_tags = list(question.tags.all())
+ current_tagnames = set(t.name for t in current_tags)
+ updated_tagnames = set(t for t in tagnames.split(' ') if t)
+ modified_tags = []
+
+ removed_tags = [t for t in current_tags
+ if t.name not in updated_tagnames]
+ if removed_tags:
+ modified_tags.extend(removed_tags)
+ question.tags.remove(*removed_tags)
+
+ added_tagnames = updated_tagnames - current_tagnames
+ if added_tagnames:
+ added_tags = Tag.objects.get_or_create_multiple(added_tagnames,
+ user)
+ modified_tags.extend(added_tags)
+ question.tags.add(*added_tags)
+
+ if modified_tags:
+ Tag.objects.update_use_counts(modified_tags)
+ return True
+
+ return False
+
+ def update_answer_count(self, question):
+ """
+ Executes an UPDATE query to update denormalised data with the
+ number of answers the given question has.
+ """
+
+ # for some reasons, this Answer class failed to be imported,
+ # although we have imported all classes from models on top.
+ from forum.models import Answer
+ self.filter(id=question.id).update(
+ answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count())
+
+ def update_view_count(self, question):
+ """
+ update counter+1 when user browse question page
+ """
+ self.filter(id=question.id).update(view_count = question.view_count + 1)
+
+ def update_favorite_count(self, question):
+ """
+ update favourite_count for given question
+ """
+ from forum.models import FavoriteQuestion
+ self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count())
+
+ def get_similar_questions(self, question):
+ """
+ Get 10 similar questions for given one.
+ This will search the same tag list for give question(by exactly same string) first.
+ 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)
+
+ #print datetime.datetime.now()
+ return questions
+
+class TagManager(models.Manager):
+ UPDATE_USED_COUNTS_QUERY = (
+ 'UPDATE tag '
+ 'SET used_count = ('
+ 'SELECT COUNT(*) FROM question_tags '
+ 'INNER JOIN question ON question_id=question.id '
+ 'WHERE tag_id = tag.id AND question.deleted=0'
+ ') '
+ 'WHERE id IN (%s)')
+
+ def get_valid_tags(self, page_size):
+ from forum.models import Tag
+ tags = Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size]
+ return tags
+
+ def get_or_create_multiple(self, names, user):
+ """
+ Fetches a list of Tags with the given names, creating any Tags
+ which don't exist when necesssary.
+ """
+ tags = list(self.filter(name__in=names))
+ #Set all these tag visible
+ for tag in tags:
+ if tag.deleted:
+ tag.deleted = False
+ tag.deleted_by = None
+ tag.deleted_at = None
+ tag.save()
+
+ if len(tags) < len(names):
+ existing_names = set(tag.name for tag in tags)
+ new_names = [name for name in names if name not in existing_names]
+ tags.extend([self.create(name=name, created_by=user)
+ for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0])
+
+ return tags
+
+ def update_use_counts(self, tags):
+ """Updates the given Tags with their current use counts."""
+ if not tags:
+ return
+ cursor = connection.cursor()
+ query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags))
+ cursor.execute(query, [tag.id for tag in tags])
+ transaction.commit_unless_managed()
+
+ def get_tags_by_questions(self, questions):
+ question_ids = []
+ for question in questions:
+ question_ids.append(question.id)
+
+ question_ids_str = ','.join([str(id) for id in question_ids])
+ related_tags = self.extra(
+ tables=['tag', 'question_tags'],
+ where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"]
+ ).distinct()
+
+ return related_tags
+
+class AnswerManager(models.Manager):
+ GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s'
+ def get_answers_from_question(self, question, user=None):
+ """
+ Retrieves visibile answers for the given question. Delete answers
+ are only visibile to the person who deleted them.
+ """
+
+ if user is None or not user.is_authenticated():
+ return self.filter(question=question, deleted=False)
+ else:
+ return self.filter(Q(question=question),
+ Q(deleted=False) | Q(deleted_by=user))
+
+ def get_answers_from_questions(self, user_id):
+ """
+ Retrieves visibile answers for the given question. Which are not included own answers
+ """
+ cursor = connection.cursor()
+ cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id])
+ return cursor.fetchall()
+
+class VoteManager(models.Manager):
+ COUNT_UP_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = 1"
+ COUNT_DOWN_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = -1"
+ COUNT_VOTES_PER_DAY_BY_USER = "SELECT COUNT(*) FROM vote WHERE user_id = %s AND DATE(voted_at) = %s"
+ def get_up_vote_count_from_user(self, user):
+ if user is not None:
+ cursor = connection.cursor()
+ cursor.execute(self.COUNT_UP_VOTE_BY_USER, [user.id])
+ row = cursor.fetchone()
+ return row[0]
+ else:
+ return 0
+
+ def get_down_vote_count_from_user(self, user):
+ if user is not None:
+ cursor = connection.cursor()
+ cursor.execute(self.COUNT_DOWN_VOTE_BY_USER, [user.id])
+ row = cursor.fetchone()
+ return row[0]
+ else:
+ return 0
+
+ def get_votes_count_today_from_user(self, user):
+ if user is not None:
+ cursor = connection.cursor()
+ cursor.execute(self.COUNT_VOTES_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
+ row = cursor.fetchone()
+ return row[0]
+
+ else:
+ return 0
+
+class FlaggedItemManager(models.Manager):
+ COUNT_FLAGS_PER_DAY_BY_USER = "SELECT COUNT(*) FROM flagged_item WHERE user_id = %s AND DATE(flagged_at) = DATE(NOW())"
+ def get_flagged_items_count_today(self, user):
+ if user is not None:
+ cursor = connection.cursor()
+ cursor.execute(self.COUNT_FLAGS_PER_DAY_BY_USER, [user.id])
+ row = cursor.fetchone()
+ return row[0]
+
+ else:
+ return 0
+
+class ReputeManager(models.Manager):
+ COUNT_REPUTATION_PER_DAY_BY_USER = "SELECT SUM(positive)+SUM(negative) FROM repute WHERE user_id = %s AND (reputation_type=1 OR reputation_type=-8) AND DATE(reputed_at) = DATE(NOW())"
+ def get_reputation_by_upvoted_today(self, user):
+ """
+ For one user in one day, he can only earn rep till certain score (ep. +200)
+ by upvoted(also substracted from upvoted canceled). This is because we need
+ to prohibit gaming system by upvoting/cancel again and again.
+ """
+ if user is not None:
+ cursor = connection.cursor()
+ cursor.execute(self.COUNT_REPUTATION_PER_DAY_BY_USER, [user.id])
+ row = cursor.fetchone()
+ return row[0]
+
+ else:
+ return 0
+class AwardManager(models.Manager):
+ def get_recent_awards(self):
+ awards = super(AwardManager, self).extra(
+ select={'badge_id': 'badge.id', 'badge_name':'badge.name',
+ 'badge_description': 'badge.description', 'badge_type': 'badge.type',
+ 'user_id': 'auth_user.id', 'user_name': 'auth_user.username'
+ },
+ tables=['award', 'badge', 'auth_user'],
+ order_by=['-awarded_at'],
+ where=['auth_user.id=award.user_id AND badge_id=badge.id'],
+ ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name')
+ return awards
--
cgit v1.2.3-1-g7c22
From e70af1088b01f059a5a989ef4640e45999af007b Mon Sep 17 00:00:00 2001
From: hrcerqueira
Date: Wed, 20 Jan 2010 19:52:56 +0000
Subject: Users no long are allowed to change their username, and the email
address is now a required field.
---
forum/forms.py | 636 +++++++++++++++++++++++------------------------
forum/views.py | 2 +-
templates/user_edit.html | 190 +++++++-------
3 files changed, 414 insertions(+), 414 deletions(-)
diff --git a/forum/forms.py b/forum/forms.py
index 376f5ddf..d727440e 100644
--- a/forum/forms.py
+++ b/forum/forms.py
@@ -1,318 +1,318 @@
-import re
-from datetime import date
-from django import forms
-from models import *
-from const import *
-from django.utils.translation import ugettext as _
-from django_authopenid.forms import NextUrlField, UserNameField
-import settings
-
-class TitleField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(TitleField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off'})
- self.max_length = 255
- self.label = _('title')
- self.help_text = _('please enter a descriptive title for your question')
- self.initial = ''
-
- def clean(self, value):
- if len(value) < 10:
- raise forms.ValidationError(_('title must be > 10 characters'))
-
- return value
-
-class EditorField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(EditorField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.Textarea(attrs={'id':'editor'})
- self.label = _('content')
- self.help_text = u''
- self.initial = ''
-
- def clean(self, value):
- if len(value) < 10:
- raise forms.ValidationError(_('question content must be > 10 characters'))
-
- return value
-
-class TagNamesField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(TagNamesField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
- self.max_length = 255
- self.label = _('tags')
- #self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
- self.help_text = _('Tags are short keywords, with no spaces within. Up to five tags can be used.')
- self.initial = ''
-
- def clean(self, value):
- value = super(TagNamesField, self).clean(value)
- data = value.strip()
- if len(data) < 1:
- raise forms.ValidationError(_('tags are required'))
-
- split_re = re.compile(r'[ ,]+')
- list = split_re.split(data)
- list_temp = []
- if len(list) > 5:
- raise forms.ValidationError(_('please use 5 tags or less'))
- for tag in list:
- if len(tag) > 20:
- raise forms.ValidationError(_('tags must be shorter than 20 characters'))
- #take tag regex from settings
- tagname_re = re.compile(r'[a-z0-9]+')
- if not tagname_re.match(tag):
- raise forms.ValidationError(_('please use following characters in tags: letters \'a-z\', numbers, and characters \'.-_#\''))
- # only keep one same tag
- if tag not in list_temp and len(tag.strip()) > 0:
- list_temp.append(tag)
- return u' '.join(list_temp)
-
-class WikiField(forms.BooleanField):
- def __init__(self, *args, **kwargs):
- super(WikiField, self).__init__(*args, **kwargs)
- self.required = False
- self.label = _('community wiki')
- self.help_text = _('if you choose community wiki option, the question and answer do not generate points and name of author will not be shown')
- def clean(self,value):
- return value and settings.WIKI_ON
-
-class EmailNotifyField(forms.BooleanField):
- def __init__(self, *args, **kwargs):
- super(EmailNotifyField, self).__init__(*args, **kwargs)
- self.required = False
- self.widget.attrs['class'] = 'nomargin'
-
-class SummaryField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(SummaryField, self).__init__(*args, **kwargs)
- self.required = False
- self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
- self.max_length = 300
- self.label = _('update summary:')
- self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)')
-
-class ModerateUserForm(forms.ModelForm):
- is_approved = forms.BooleanField(label=_("Automatically accept user's contributions for the email updates"),
- required=False)
-
- def clean_is_approved(self):
- if 'is_approved' not in self.cleaned_data:
- self.cleaned_data['is_approved'] = False
- return self.cleaned_data['is_approved']
-
- class Meta:
- model = User
- fields = ('is_approved',)
-
-class FeedbackForm(forms.Form):
- name = forms.CharField(label=_('Your name:'), required=False)
- email = forms.EmailField(label=_('Email (not shared with anyone):'), required=False)
- message = forms.CharField(label=_('Your message:'), max_length=800,widget=forms.Textarea(attrs={'cols':60}))
- next = NextUrlField()
-
-class AskForm(forms.Form):
- title = TitleField()
- text = EditorField()
- tags = TagNamesField()
- wiki = WikiField()
-
- openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
- user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
-
-class AnswerForm(forms.Form):
- text = EditorField()
- wiki = WikiField()
- openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
- user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email_notify = EmailNotifyField()
- def __init__(self, question, user, *args, **kwargs):
- super(AnswerForm, self).__init__(*args, **kwargs)
- self.fields['email_notify'].widget.attrs['id'] = 'question-subscribe-updates';
- if question.wiki and settings.WIKI_ON:
- self.fields['wiki'].initial = True
- if user.is_authenticated():
- if user in question.followed_by.all():
- self.fields['email_notify'].initial = True
- return
- self.fields['email_notify'].initial = False
-
-
-class CloseForm(forms.Form):
- reason = forms.ChoiceField(choices=CLOSE_REASONS)
-
-class RetagQuestionForm(forms.Form):
- tags = TagNamesField()
- # initialize the default values
- def __init__(self, question, *args, **kwargs):
- super(RetagQuestionForm, self).__init__(*args, **kwargs)
- self.fields['tags'].initial = question.tagnames
-
-class RevisionForm(forms.Form):
- """
- Lists revisions of a Question or Answer
- """
- revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'}))
-
- def __init__(self, post, latest_revision, *args, **kwargs):
- super(RevisionForm, self).__init__(*args, **kwargs)
- revisions = post.revisions.all().values_list(
- 'revision', 'author__username', 'revised_at', 'summary')
- date_format = '%c'
- self.fields['revision'].choices = [
- (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3]))
- for r in revisions]
- self.fields['revision'].initial = latest_revision.revision
-
-class EditQuestionForm(forms.Form):
- title = TitleField()
- text = EditorField()
- tags = TagNamesField()
- summary = SummaryField()
-
- def __init__(self, question, revision, *args, **kwargs):
- super(EditQuestionForm, self).__init__(*args, **kwargs)
- self.fields['title'].initial = revision.title
- self.fields['text'].initial = revision.text
- self.fields['tags'].initial = revision.tagnames
- # Once wiki mode is enabled, it can't be disabled
- if not question.wiki:
- self.fields['wiki'] = WikiField()
-
-class EditAnswerForm(forms.Form):
- text = EditorField()
- summary = SummaryField()
-
- def __init__(self, answer, revision, *args, **kwargs):
- super(EditAnswerForm, self).__init__(*args, **kwargs)
- self.fields['text'].initial = revision.text
-
-class EditUserForm(forms.Form):
- email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- username = UserNameField(label=_('Screen name'))
- realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- birthday = forms.DateField(label=_('Date of birth'), help_text=_('will not be shown, used to calculate age, format: YYYY-MM-DD'), required=False, widget=forms.TextInput(attrs={'size' : 35}))
- about = forms.CharField(label=_('Profile'), required=False, widget=forms.Textarea(attrs={'cols' : 60}))
-
- def __init__(self, user, *args, **kwargs):
- super(EditUserForm, self).__init__(*args, **kwargs)
- self.fields['username'].initial = user.username
- self.fields['username'].user_instance = user
- self.fields['email'].initial = user.email
- self.fields['realname'].initial = user.real_name
- self.fields['website'].initial = user.website
- self.fields['city'].initial = user.location
-
- if user.date_of_birth is not None:
- self.fields['birthday'].initial = user.date_of_birth
- else:
- self.fields['birthday'].initial = '1990-01-01'
- self.fields['about'].initial = user.about
- self.user = user
-
- def clean_email(self):
- """For security reason one unique email in database"""
- if self.user.email != self.cleaned_data['email']:
- #todo dry it, there is a similar thing in openidauth
- if settings.EMAIL_UNIQUE == True:
- if 'email' in self.cleaned_data:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- except User.DoesNotExist:
- return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- return self.cleaned_data['email']
-
-class TagFilterSelectionForm(forms.ModelForm):
- tag_filter_setting = forms.ChoiceField(choices=TAG_EMAIL_FILTER_CHOICES, #imported from forum/const.py
- initial='ignored',
- label=_('Choose email tag filter'),
- widget=forms.RadioSelect)
- class Meta:
- model = User
- fields = ('tag_filter_setting',)
-
- def save(self):
- before = self.instance.tag_filter_setting
- super(TagFilterSelectionForm, self).save()
- after = self.instance.tag_filter_setting #User.objects.get(pk=self.instance.id).tag_filter_setting
- if before != after:
- return True
- return False
-
-class EditUserEmailFeedsForm(forms.Form):
- WN = (('w',_('weekly')),('n',_('no email')))
- DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
- FORM_TO_MODEL_MAP = {
- 'all_questions':'q_all',
- 'asked_by_me':'q_ask',
- 'answered_by_me':'q_ans',
- 'individually_selected':'q_sel',
- }
- NO_EMAIL_INITIAL = {
- 'all_questions':'n',
- 'asked_by_me':'n',
- 'answered_by_me':'n',
- 'individually_selected':'n',
- }
- asked_by_me = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Asked by me'))
- answered_by_me = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Answered by me'))
- individually_selected = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Individually selected'))
- all_questions = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Entire forum (tag filtered)'),)
-
- def set_initial_values(self,user=None):
- KEY_MAP = dict([(v,k) for k,v in self.FORM_TO_MODEL_MAP.iteritems()])
- if user != None:
- settings = EmailFeedSetting.objects.filter(subscriber=user)
- initial_values = {}
- for setting in settings:
- feed_type = setting.feed_type
- form_field = KEY_MAP[feed_type]
- frequency = setting.frequency
- initial_values[form_field] = frequency
- self.initial = initial_values
- return self
-
- def reset(self):
- self.cleaned_data['all_questions'] = 'n'
- self.cleaned_data['asked_by_me'] = 'n'
- self.cleaned_data['answered_by_me'] = 'n'
- self.cleaned_data['individually_selected'] = 'n'
- self.initial = self.NO_EMAIL_INITIAL
- return self
-
- def save(self,user):
- changed = False
- for form_field, feed_type in self.FORM_TO_MODEL_MAP.items():
- s, created = EmailFeedSetting.objects.get_or_create(subscriber=user,\
- feed_type=feed_type)
- new_value = self.cleaned_data[form_field]
- if s.frequency != new_value:
- s.frequency = self.cleaned_data[form_field]
- s.save()
- changed = True
- else:
- if created:
- s.save()
- if form_field == 'individually_selected':
- feed_type = ContentType.objects.get_for_model(Question)
- user.followed_questions.clear()
- return changed
+import re
+from datetime import date
+from django import forms
+from models import *
+from const import *
+from django.utils.translation import ugettext as _
+from django_authopenid.forms import NextUrlField, UserNameField
+import settings
+
+class TitleField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ super(TitleField, self).__init__(*args, **kwargs)
+ self.required = True
+ self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off'})
+ self.max_length = 255
+ self.label = _('title')
+ self.help_text = _('please enter a descriptive title for your question')
+ self.initial = ''
+
+ def clean(self, value):
+ if len(value) < 10:
+ raise forms.ValidationError(_('title must be > 10 characters'))
+
+ return value
+
+class EditorField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ super(EditorField, self).__init__(*args, **kwargs)
+ self.required = True
+ self.widget = forms.Textarea(attrs={'id':'editor'})
+ self.label = _('content')
+ self.help_text = u''
+ self.initial = ''
+
+ def clean(self, value):
+ if len(value) < 10:
+ raise forms.ValidationError(_('question content must be > 10 characters'))
+
+ return value
+
+class TagNamesField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ super(TagNamesField, self).__init__(*args, **kwargs)
+ self.required = True
+ self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
+ self.max_length = 255
+ self.label = _('tags')
+ #self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
+ self.help_text = _('Tags are short keywords, with no spaces within. Up to five tags can be used.')
+ self.initial = ''
+
+ def clean(self, value):
+ value = super(TagNamesField, self).clean(value)
+ data = value.strip()
+ if len(data) < 1:
+ raise forms.ValidationError(_('tags are required'))
+
+ split_re = re.compile(r'[ ,]+')
+ list = split_re.split(data)
+ list_temp = []
+ if len(list) > 5:
+ raise forms.ValidationError(_('please use 5 tags or less'))
+ for tag in list:
+ if len(tag) > 20:
+ raise forms.ValidationError(_('tags must be shorter than 20 characters'))
+ #take tag regex from settings
+ tagname_re = re.compile(r'[a-z0-9]+')
+ if not tagname_re.match(tag):
+ raise forms.ValidationError(_('please use following characters in tags: letters \'a-z\', numbers, and characters \'.-_#\''))
+ # only keep one same tag
+ if tag not in list_temp and len(tag.strip()) > 0:
+ list_temp.append(tag)
+ return u' '.join(list_temp)
+
+class WikiField(forms.BooleanField):
+ def __init__(self, *args, **kwargs):
+ super(WikiField, self).__init__(*args, **kwargs)
+ self.required = False
+ self.label = _('community wiki')
+ self.help_text = _('if you choose community wiki option, the question and answer do not generate points and name of author will not be shown')
+ def clean(self,value):
+ return value and settings.WIKI_ON
+
+class EmailNotifyField(forms.BooleanField):
+ def __init__(self, *args, **kwargs):
+ super(EmailNotifyField, self).__init__(*args, **kwargs)
+ self.required = False
+ self.widget.attrs['class'] = 'nomargin'
+
+class SummaryField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ super(SummaryField, self).__init__(*args, **kwargs)
+ self.required = False
+ self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
+ self.max_length = 300
+ self.label = _('update summary:')
+ self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)')
+
+class ModerateUserForm(forms.ModelForm):
+ is_approved = forms.BooleanField(label=_("Automatically accept user's contributions for the email updates"),
+ required=False)
+
+ def clean_is_approved(self):
+ if 'is_approved' not in self.cleaned_data:
+ self.cleaned_data['is_approved'] = False
+ return self.cleaned_data['is_approved']
+
+ class Meta:
+ model = User
+ fields = ('is_approved',)
+
+class FeedbackForm(forms.Form):
+ name = forms.CharField(label=_('Your name:'), required=False)
+ email = forms.EmailField(label=_('Email (not shared with anyone):'), required=False)
+ message = forms.CharField(label=_('Your message:'), max_length=800,widget=forms.Textarea(attrs={'cols':60}))
+ next = NextUrlField()
+
+class AskForm(forms.Form):
+ title = TitleField()
+ text = EditorField()
+ tags = TagNamesField()
+ wiki = WikiField()
+
+ openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
+ user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+
+class AnswerForm(forms.Form):
+ text = EditorField()
+ wiki = WikiField()
+ openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
+ user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ email_notify = EmailNotifyField()
+ def __init__(self, question, user, *args, **kwargs):
+ super(AnswerForm, self).__init__(*args, **kwargs)
+ self.fields['email_notify'].widget.attrs['id'] = 'question-subscribe-updates';
+ if question.wiki and settings.WIKI_ON:
+ self.fields['wiki'].initial = True
+ if user.is_authenticated():
+ if user in question.followed_by.all():
+ self.fields['email_notify'].initial = True
+ return
+ self.fields['email_notify'].initial = False
+
+
+class CloseForm(forms.Form):
+ reason = forms.ChoiceField(choices=CLOSE_REASONS)
+
+class RetagQuestionForm(forms.Form):
+ tags = TagNamesField()
+ # initialize the default values
+ def __init__(self, question, *args, **kwargs):
+ super(RetagQuestionForm, self).__init__(*args, **kwargs)
+ self.fields['tags'].initial = question.tagnames
+
+class RevisionForm(forms.Form):
+ """
+ Lists revisions of a Question or Answer
+ """
+ revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'}))
+
+ def __init__(self, post, latest_revision, *args, **kwargs):
+ super(RevisionForm, self).__init__(*args, **kwargs)
+ revisions = post.revisions.all().values_list(
+ 'revision', 'author__username', 'revised_at', 'summary')
+ date_format = '%c'
+ self.fields['revision'].choices = [
+ (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3]))
+ for r in revisions]
+ self.fields['revision'].initial = latest_revision.revision
+
+class EditQuestionForm(forms.Form):
+ title = TitleField()
+ text = EditorField()
+ tags = TagNamesField()
+ summary = SummaryField()
+
+ def __init__(self, question, revision, *args, **kwargs):
+ super(EditQuestionForm, self).__init__(*args, **kwargs)
+ self.fields['title'].initial = revision.title
+ self.fields['text'].initial = revision.text
+ self.fields['tags'].initial = revision.tagnames
+ # Once wiki mode is enabled, it can't be disabled
+ if not question.wiki:
+ self.fields['wiki'] = WikiField()
+
+class EditAnswerForm(forms.Form):
+ text = EditorField()
+ summary = SummaryField()
+
+ def __init__(self, answer, revision, *args, **kwargs):
+ super(EditAnswerForm, self).__init__(*args, **kwargs)
+ self.fields['text'].initial = revision.text
+
+class EditUserForm(forms.Form):
+ email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=True, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ #username = UserNameField(label=_('Screen name'))
+ realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
+ birthday = forms.DateField(label=_('Date of birth'), help_text=_('will not be shown, used to calculate age, format: YYYY-MM-DD'), required=False, widget=forms.TextInput(attrs={'size' : 35}))
+ about = forms.CharField(label=_('Profile'), required=False, widget=forms.Textarea(attrs={'cols' : 60}))
+
+ def __init__(self, user, *args, **kwargs):
+ super(EditUserForm, self).__init__(*args, **kwargs)
+ #self.fields['username'].initial = user.username
+ #self.fields['username'].user_instance = user
+ self.fields['email'].initial = user.email
+ self.fields['realname'].initial = user.real_name
+ self.fields['website'].initial = user.website
+ self.fields['city'].initial = user.location
+
+ if user.date_of_birth is not None:
+ self.fields['birthday'].initial = user.date_of_birth
+ else:
+ self.fields['birthday'].initial = '1990-01-01'
+ self.fields['about'].initial = user.about
+ self.user = user
+
+ def clean_email(self):
+ """For security reason one unique email in database"""
+ if self.user.email != self.cleaned_data['email']:
+ #todo dry it, there is a similar thing in openidauth
+ if settings.EMAIL_UNIQUE == True:
+ if 'email' in self.cleaned_data:
+ try:
+ user = User.objects.get(email = self.cleaned_data['email'])
+ except User.DoesNotExist:
+ return self.cleaned_data['email']
+ except User.MultipleObjectsReturned:
+ raise forms.ValidationError(_('this email has already been registered, please use another one'))
+ raise forms.ValidationError(_('this email has already been registered, please use another one'))
+ return self.cleaned_data['email']
+
+class TagFilterSelectionForm(forms.ModelForm):
+ tag_filter_setting = forms.ChoiceField(choices=TAG_EMAIL_FILTER_CHOICES, #imported from forum/const.py
+ initial='ignored',
+ label=_('Choose email tag filter'),
+ widget=forms.RadioSelect)
+ class Meta:
+ model = User
+ fields = ('tag_filter_setting',)
+
+ def save(self):
+ before = self.instance.tag_filter_setting
+ super(TagFilterSelectionForm, self).save()
+ after = self.instance.tag_filter_setting #User.objects.get(pk=self.instance.id).tag_filter_setting
+ if before != after:
+ return True
+ return False
+
+class EditUserEmailFeedsForm(forms.Form):
+ WN = (('w',_('weekly')),('n',_('no email')))
+ DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
+ FORM_TO_MODEL_MAP = {
+ 'all_questions':'q_all',
+ 'asked_by_me':'q_ask',
+ 'answered_by_me':'q_ans',
+ 'individually_selected':'q_sel',
+ }
+ NO_EMAIL_INITIAL = {
+ 'all_questions':'n',
+ 'asked_by_me':'n',
+ 'answered_by_me':'n',
+ 'individually_selected':'n',
+ }
+ asked_by_me = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Asked by me'))
+ answered_by_me = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Answered by me'))
+ individually_selected = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Individually selected'))
+ all_questions = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Entire forum (tag filtered)'),)
+
+ def set_initial_values(self,user=None):
+ KEY_MAP = dict([(v,k) for k,v in self.FORM_TO_MODEL_MAP.iteritems()])
+ if user != None:
+ settings = EmailFeedSetting.objects.filter(subscriber=user)
+ initial_values = {}
+ for setting in settings:
+ feed_type = setting.feed_type
+ form_field = KEY_MAP[feed_type]
+ frequency = setting.frequency
+ initial_values[form_field] = frequency
+ self.initial = initial_values
+ return self
+
+ def reset(self):
+ self.cleaned_data['all_questions'] = 'n'
+ self.cleaned_data['asked_by_me'] = 'n'
+ self.cleaned_data['answered_by_me'] = 'n'
+ self.cleaned_data['individually_selected'] = 'n'
+ self.initial = self.NO_EMAIL_INITIAL
+ return self
+
+ def save(self,user):
+ changed = False
+ for form_field, feed_type in self.FORM_TO_MODEL_MAP.items():
+ s, created = EmailFeedSetting.objects.get_or_create(subscriber=user,\
+ feed_type=feed_type)
+ new_value = self.cleaned_data[form_field]
+ if s.frequency != new_value:
+ s.frequency = self.cleaned_data[form_field]
+ s.save()
+ changed = True
+ else:
+ if created:
+ s.save()
+ if form_field == 'individually_selected':
+ feed_type = ContentType.objects.get_for_model(Question)
+ user.followed_questions.clear()
+ return changed
diff --git a/forum/views.py b/forum/views.py
index f5eb6598..04d9d497 100644
--- a/forum/views.py
+++ b/forum/views.py
@@ -1235,7 +1235,7 @@ def edit_user(request, id):
from django_authopenid.views import set_new_email
set_new_email(user, new_email)
- user.username = sanitize_html(form.cleaned_data['username'])
+ #user.username = sanitize_html(form.cleaned_data['username'])
user.real_name = sanitize_html(form.cleaned_data['realname'])
user.website = sanitize_html(form.cleaned_data['website'])
user.location = sanitize_html(form.cleaned_data['city'])
diff --git a/templates/user_edit.html b/templates/user_edit.html
index 5886c071..bc5056f9 100644
--- a/templates/user_edit.html
+++ b/templates/user_edit.html
@@ -1,95 +1,95 @@
-{% extends "base_content.html" %}
-
-{% load extra_tags %}
-{% load humanize %}
-{% load i18n %}
-{% block title %}{% spaceless %}{% trans "Edit user profile" %}{% endspaceless %}{% endblock %}
-{% block forejs %}
-
- {% block userjs %}
- {% endblock %}
-{% endblock %}
-{% block content %}
-
+{% endblock %}
+
--
cgit v1.2.3-1-g7c22
From 07839eccb217d21f93c377fd275053ab72ab855c Mon Sep 17 00:00:00 2001
From: hrcerqueira
Date: Wed, 20 Jan 2010 20:06:05 +0000
Subject: Some more sql fixes, I'm prety that now osqa works with sqlite as
well.
---
forum/managers.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/forum/managers.py b/forum/managers.py
index ce67c237..ba174998 100644
--- a/forum/managers.py
+++ b/forum/managers.py
@@ -200,11 +200,11 @@ class VoteManager(models.Manager):
return 0
class FlaggedItemManager(models.Manager):
- COUNT_FLAGS_PER_DAY_BY_USER = "SELECT COUNT(*) FROM flagged_item WHERE user_id = %s AND DATE(flagged_at) = DATE(NOW())"
+ COUNT_FLAGS_PER_DAY_BY_USER = "SELECT COUNT(*) FROM flagged_item WHERE user_id = %s AND DATE(flagged_at) = %s"
def get_flagged_items_count_today(self, user):
if user is not None:
cursor = connection.cursor()
- cursor.execute(self.COUNT_FLAGS_PER_DAY_BY_USER, [user.id])
+ cursor.execute(self.COUNT_FLAGS_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
row = cursor.fetchone()
return row[0]
@@ -212,7 +212,7 @@ class FlaggedItemManager(models.Manager):
return 0
class ReputeManager(models.Manager):
- COUNT_REPUTATION_PER_DAY_BY_USER = "SELECT SUM(positive)+SUM(negative) FROM repute WHERE user_id = %s AND (reputation_type=1 OR reputation_type=-8) AND DATE(reputed_at) = DATE(NOW())"
+ COUNT_REPUTATION_PER_DAY_BY_USER = "SELECT SUM(positive)+SUM(negative) FROM repute WHERE user_id = %s AND (reputation_type=1 OR reputation_type=-8) AND DATE(reputed_at) = %s"
def get_reputation_by_upvoted_today(self, user):
"""
For one user in one day, he can only earn rep till certain score (ep. +200)
@@ -221,7 +221,7 @@ class ReputeManager(models.Manager):
"""
if user is not None:
cursor = connection.cursor()
- cursor.execute(self.COUNT_REPUTATION_PER_DAY_BY_USER, [user.id])
+ cursor.execute(self.COUNT_REPUTATION_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
row = cursor.fetchone()
return row[0]
--
cgit v1.2.3-1-g7c22
From c18bda49f13afb4e2eaaf9ccb35098ee174e3afc Mon Sep 17 00:00:00 2001
From: hrcerqueira
Date: Thu, 21 Jan 2010 13:07:11 +0000
Subject: Added the basic html and javascript of fb connect, and a setting in
settings_local.py.dist for the fb api key.
---
django_authopenid/urls.py | 64 +-
django_authopenid/views.py | 2141 +++++++++++++++++++-------------------
settings_local.py.dist | 179 ++--
templates/authopenid/signin.html | 351 ++++---
templates/base.html | 190 ++--
templates/xd_receiver.html | 1 +
6 files changed, 1469 insertions(+), 1457 deletions(-)
mode change 100644 => 100755 django_authopenid/urls.py
mode change 100644 => 100755 django_authopenid/views.py
mode change 100644 => 100755 templates/authopenid/signin.html
mode change 100644 => 100755 templates/base.html
create mode 100755 templates/xd_receiver.html
diff --git a/django_authopenid/urls.py b/django_authopenid/urls.py
old mode 100644
new mode 100755
index 112cbbe1..6a4e9b0f
--- a/django_authopenid/urls.py
+++ b/django_authopenid/urls.py
@@ -1,31 +1,33 @@
-# -*- coding: utf-8 -*-
-from django.conf.urls.defaults import patterns, url
-from django.utils.translation import ugettext as _
-
-urlpatterns = patterns('django_authopenid.views',
- # yadis rdf
- url(r'^yadis.xrdf$', 'xrdf', name='yadis_xrdf'),
- # manage account registration
- url(r'^%s$' % _('signin/'), 'signin', name='user_signin'),
- url(r'^%s%s$' % (_('signin/'),_('newquestion/')), 'signin', kwargs = {'newquestion':True}, name='user_signin_new_question'),
- url(r'^%s%s$' % (_('signin/'),_('newanswer/')), 'signin', kwargs = {'newanswer':True}, name='user_signin_new_answer'),
- url(r'^%s$' % _('signout/'), 'signout', name='user_signout'),
- url(r'^%s%s$' % (_('signin/'), _('complete/')), 'complete_signin',
- name='user_complete_signin'),
- url('^%s$' % _('external-login/'),'external_legacy_login_info', name='user_external_legacy_login_issues'),
- url(r'^%s$' % _('register/'), 'register', name='user_register'),
- url(r'^%s$' % _('signup/'), 'signup', name='user_signup'),
- #disable current sendpw function
- url(r'^%s$' % _('sendpw/'), 'sendpw', name='user_sendpw'),
- url(r'^%s%s$' % (_('password/'), _('confirm/')), 'confirmchangepw', name='user_confirmchangepw'),
-
- # manage account settings
- url(r'^$', _('account_settings'), name='user_account_settings'),
- url(r'^%s$' % _('password/'), 'changepw', name='user_changepw'),
- url(r'^%s%s$' % (_('email/'),_('validate/')), 'changeemail', name='user_validateemail',kwargs = {'action':'validate'}),
- url(r'^%s%s$' % (_('email/'), _('change/')), 'changeemail', name='user_changeemail'),
- url(r'^%s%s$' % (_('email/'), _('sendkey/')), 'send_email_key', name='send_email_key'),
- url(r'^%s%s(?P\d+)/(?P[\dabcdef]{32})/$' % (_('email/'), _('verify/')), 'verifyemail', name='user_verifyemail'),
- url(r'^%s$' % _('openid/'), 'changeopenid', name='user_changeopenid'),
- url(r'^%s$' % _('delete/'), 'delete', name='user_delete'),
-)
+# -*- coding: utf-8 -*-
+from django.conf.urls.defaults import patterns, url
+from django.utils.translation import ugettext as _
+from django.views.generic.simple import direct_to_template
+
+urlpatterns = patterns('django_authopenid.views',
+ # yadis rdf
+ url(r'^yadis.xrdf$', 'xrdf', name='yadis_xrdf'),
+ # manage account registration
+ url(r'^%s$' % _('signin/'), 'signin', name='user_signin'),
+ url(r'^xd_receiver$', direct_to_template, {'template': 'xd_receiver.html'}, name='xd_receiver'),
+ url(r'^%s%s$' % (_('signin/'),_('newquestion/')), 'signin', kwargs = {'newquestion':True}, name='user_signin_new_question'),
+ url(r'^%s%s$' % (_('signin/'),_('newanswer/')), 'signin', kwargs = {'newanswer':True}, name='user_signin_new_answer'),
+ url(r'^%s$' % _('signout/'), 'signout', name='user_signout'),
+ url(r'^%s%s$' % (_('signin/'), _('complete/')), 'complete_signin',
+ name='user_complete_signin'),
+ url('^%s$' % _('external-login/'),'external_legacy_login_info', name='user_external_legacy_login_issues'),
+ url(r'^%s$' % _('register/'), 'register', name='user_register'),
+ url(r'^%s$' % _('signup/'), 'signup', name='user_signup'),
+ #disable current sendpw function
+ url(r'^%s$' % _('sendpw/'), 'sendpw', name='user_sendpw'),
+ url(r'^%s%s$' % (_('password/'), _('confirm/')), 'confirmchangepw', name='user_confirmchangepw'),
+
+ # manage account settings
+ url(r'^$', _('account_settings'), name='user_account_settings'),
+ url(r'^%s$' % _('password/'), 'changepw', name='user_changepw'),
+ url(r'^%s%s$' % (_('email/'),_('validate/')), 'changeemail', name='user_validateemail',kwargs = {'action':'validate'}),
+ url(r'^%s%s$' % (_('email/'), _('change/')), 'changeemail', name='user_changeemail'),
+ url(r'^%s%s$' % (_('email/'), _('sendkey/')), 'send_email_key', name='send_email_key'),
+ url(r'^%s%s(?P\d+)/(?P[\dabcdef]{32})/$' % (_('email/'), _('verify/')), 'verifyemail', name='user_verifyemail'),
+ url(r'^%s$' % _('openid/'), 'changeopenid', name='user_changeopenid'),
+ url(r'^%s$' % _('delete/'), 'delete', name='user_delete'),
+)
diff --git a/django_authopenid/views.py b/django_authopenid/views.py
old mode 100644
new mode 100755
index feb6b58f..36f08018
--- a/django_authopenid/views.py
+++ b/django_authopenid/views.py
@@ -1,1070 +1,1071 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2007, 2008, Benoît Chesneau
-# Copyright (c) 2007 Simon Willison, original work on django-openid
-#
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# * notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# * notice, this list of conditions and the following disclaimer in the
-# * documentation and/or other materials provided with the
-# * distribution. Neither the name of the nor the names
-# * of its contributors may be used to endorse or promote products
-# * derived from this software without specific prior written
-# * permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from django.http import HttpResponseRedirect, get_host, Http404, \
- HttpResponseServerError
-from django.shortcuts import render_to_response as render
-from django.template import RequestContext, loader, Context
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth import authenticate
-from django.core.urlresolvers import reverse
-from django.utils.encoding import smart_unicode
-from django.utils.html import escape
-from django.utils.translation import ugettext as _
-from django.utils.http import urlquote_plus
-from django.utils.safestring import mark_safe
-from django.core.mail import send_mail
-from django.views.defaults import server_error
-
-from openid.consumer.consumer import Consumer, \
- SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
-from openid.consumer.discover import DiscoveryFailure
-from openid.extensions import sreg
-# needed for some linux distributions like debian
-try:
- from openid.yadis import xri
-except ImportError:
- from yadis import xri
-
-import re
-import urllib
-
-
-from forum.forms import EditUserEmailFeedsForm
-from django_authopenid.util import OpenID, DjangoOpenIDStore, from_openid_response, get_next_url
-from django_authopenid.models import UserAssociation, UserPasswordQueue, ExternalLoginData
-from django_authopenid.forms import OpenidSigninForm, ClassicLoginForm, OpenidRegisterForm, \
- OpenidVerifyForm, ClassicRegisterForm, ChangePasswordForm, ChangeEmailForm, \
- ChangeopenidForm, DeleteForm, EmailPasswordForm
-import external_login
-import logging
-
-def login(request,user):
- from django.contrib.auth import login as _login
- from forum.models import user_logged_in #custom signal
-
- print 'in login call'
-
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- external_login.login(request,user)
-
- #1) get old session key
- session_key = request.session.session_key
- #2) login and get new session key
- _login(request,user)
- #3) send signal with old session key as argument
- user_logged_in.send(user=user,session_key=session_key,sender=None)
-
-def logout(request):
- from django.contrib.auth import logout as _logout#for login I've added wrapper below - called login
- _logout(request)
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- external_login.logout(request)
-
-def get_url_host(request):
- if request.is_secure():
- protocol = 'https'
- else:
- protocol = 'http'
- host = escape(get_host(request))
- return '%s://%s' % (protocol, host)
-
-def get_full_url(request):
- return get_url_host(request) + request.get_full_path()
-
-def ask_openid(request, openid_url, redirect_to, on_failure=None,
- sreg_request=None):
- """ basic function to ask openid and return response """
- request.encoding = 'UTF-8'
- on_failure = on_failure or signin_failure
-
- trust_root = getattr(
- settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/'
- )
- if xri.identifierScheme(openid_url) == 'XRI' and getattr(
- settings, 'OPENID_DISALLOW_INAMES', False
- ):
- msg = _("i-names are not supported")
- return on_failure(request, msg)
- consumer = Consumer(request.session, DjangoOpenIDStore())
- try:
- auth_request = consumer.begin(openid_url)
- except DiscoveryFailure:
- msg = _(u"OpenID %(openid_url)s is invalid" % {'openid_url':openid_url})
- return on_failure(request, msg)
-
- if sreg_request:
- auth_request.addExtension(sreg_request)
- redirect_url = auth_request.redirectURL(trust_root, redirect_to)
- return HttpResponseRedirect(redirect_url)
-
-def complete(request, on_success=None, on_failure=None, return_to=None):
- """ complete openid signin """
- on_success = on_success or default_on_success
- on_failure = on_failure or default_on_failure
-
- consumer = Consumer(request.session, DjangoOpenIDStore())
- # make sure params are encoded in utf8
- params = dict((k,smart_unicode(v)) for k, v in request.GET.items())
- openid_response = consumer.complete(params, return_to)
-
- if openid_response.status == SUCCESS:
- return on_success(request, openid_response.identity_url,
- openid_response)
- elif openid_response.status == CANCEL:
- return on_failure(request, 'The request was canceled')
- elif openid_response.status == FAILURE:
- return on_failure(request, openid_response.message)
- elif openid_response.status == SETUP_NEEDED:
- return on_failure(request, 'Setup needed')
- else:
- assert False, "Bad openid status: %s" % openid_response.status
-
-def default_on_success(request, identity_url, openid_response):
- """ default action on openid signin success """
- request.session['openid'] = from_openid_response(openid_response)
- return HttpResponseRedirect(get_next_url(request))
-
-def default_on_failure(request, message):
- """ default failure action on signin """
- return render('openid_failure.html', {
- 'message': message
- })
-
-
-def not_authenticated(func):
- """ decorator that redirect user to next page if
- he is already logged."""
- def decorated(request, *args, **kwargs):
- if request.user.is_authenticated():
- return HttpResponseRedirect(get_next_url(request))
- return func(request, *args, **kwargs)
- return decorated
-
-@not_authenticated
-def signin(request,newquestion=False,newanswer=False):
- """
- signin page. It manages the legacy authentification (user/password)
- and openid authentification
-
- url: /signin/
-
- template : authopenid/signin.htm
- """
- request.encoding = 'UTF-8'
- on_failure = signin_failure
- email_feeds_form = EditUserEmailFeedsForm()
- next = get_next_url(request)
- form_signin = OpenidSigninForm(initial={'next':next})
- form_auth = ClassicLoginForm(initial={'next':next})
-
- if request.POST:
- #'blogin' - password login
- if 'blogin' in request.POST.keys():
- form_auth = ClassicLoginForm(request.POST)
- if form_auth.is_valid():
- #have login and password and need to login through external website
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- username = form_auth.cleaned_data['username']
- password = form_auth.cleaned_data['password']
- next = form_auth.cleaned_data['next']
- if form_auth.get_user() == None:
- #need to create internal user
-
- #1) save login and password temporarily in session
- request.session['external_username'] = username
- request.session['external_password'] = password
-
- #2) see if username clashes with some existing user
- #if so, we have to prompt the user to pick a different name
- username_taken = User.is_username_taken(username)
- #try:
- # User.objects.get(username=username)
- # username_taken = True
- #except User.DoesNotExist:
- # username_taken = False
-
- #3) try to extract user email from external service
- email = external_login.get_email(username,password)
-
- email_feeds_form = EditUserEmailFeedsForm()
- form_data = {'username':username,'email':email,'next':next}
- form = OpenidRegisterForm(initial=form_data)
- template_data = {'form1':form,'username':username,\
- 'email_feeds_form':email_feeds_form,\
- 'provider':mark_safe(settings.EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME),\
- 'login_type':'legacy',\
- 'gravatar_faq_url':reverse('faq') + '#gravatar',\
- 'external_login_name_is_taken':username_taken}
- return render('authopenid/complete.html',template_data,\
- context_instance=RequestContext(request))
- else:
- #user existed, external password is ok
- user = form_auth.get_user()
- login(request,user)
- response = HttpResponseRedirect(get_next_url(request))
- external_login.set_login_cookies(response,user)
- return response
- else:
- #regular password authentication
- user = form_auth.get_user()
- login(request, user)
- return HttpResponseRedirect(get_next_url(request))
-
- elif 'bnewaccount' in request.POST.keys():
- #register externally logged in password user with a new local account
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- form = OpenidRegisterForm(request.POST)
- email_feeds_form = EditUserEmailFeedsForm(request.POST)
- form1_is_valid = form.is_valid()
- form2_is_valid = email_feeds_form.is_valid()
- if form1_is_valid and form2_is_valid:
- #create the user
- username = form.cleaned_data['username']
- password = request.session.get('external_password',None)
- email = form.cleaned_data['email']
- print 'got email addr %s' % email
- if password and username:
- User.objects.create_user(username,email,password)
- user = authenticate(username=username,password=password)
- external_username = request.session['external_username']
- eld = ExternalLoginData.objects.get(external_username=external_username)
- eld.user = user
- eld.save()
- login(request,user)
- email_feeds_form.save(user)
- del request.session['external_username']
- del request.session['external_password']
- return HttpResponseRedirect(reverse('index'))
- else:
- if password:
- del request.session['external_username']
- if username:
- del request.session['external_password']
- return HttpResponseServerError()
- else:
- username = request.POST.get('username',None)
- provider = mark_safe(settings.EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME)
- username_taken = User.is_username_taken(username)
- data = {'login_type':'legacy','form1':form,'username':username,\
- 'email_feeds_form':email_feeds_form,'provider':provider,\
- 'gravatar_faq_url':reverse('faq') + '#gravatar',\
- 'external_login_name_is_taken':username_taken}
- return render('authopenid/complete.html',data,
- context_instance=RequestContext(request))
- else:
- raise Http404
-
- elif 'bsignin' in request.POST.keys() or 'openid_username' in request.POST.keys():
- form_signin = OpenidSigninForm(request.POST)
- if form_signin.is_valid():
- next = form_signin.cleaned_data['next']
- sreg_req = sreg.SRegRequest(optional=['nickname', 'email'])
- redirect_to = "%s%s?%s" % (
- get_url_host(request),
- reverse('user_complete_signin'),
- urllib.urlencode({'next':next})
- )
- return ask_openid(request,
- form_signin.cleaned_data['openid_url'],
- redirect_to,
- on_failure=signin_failure,
- sreg_request=sreg_req)
-
-
- #if request is GET
- question = None
- if newquestion == True:
- from forum.models import AnonymousQuestion as AQ
- session_key = request.session.session_key
- qlist = AQ.objects.filter(session_key=session_key).order_by('-added_at')
- if len(qlist) > 0:
- question = qlist[0]
- answer = None
- if newanswer == True:
- from forum.models import AnonymousAnswer as AA
- session_key = request.session.session_key
- alist = AA.objects.filter(session_key=session_key).order_by('-added_at')
- if len(alist) > 0:
- answer = alist[0]
-
- return render('authopenid/signin.html', {
- 'question':question,
- 'answer':answer,
- 'form1': form_auth,
- 'form2': form_signin,
- 'msg': request.GET.get('msg',''),
- 'sendpw_url': reverse('user_sendpw'),
- }, context_instance=RequestContext(request))
-
-def complete_signin(request):
- """ in case of complete signin with openid """
- return complete(request, signin_success, signin_failure,
- get_url_host(request) + reverse('user_complete_signin'))
-
-def signin_success(request, identity_url, openid_response):
- """
- openid signin success.
-
- If the openid is already registered, the user is redirected to
- url set par next or in settings with OPENID_REDIRECT_NEXT variable.
- If none of these urls are set user is redirectd to /.
-
- if openid isn't registered user is redirected to register page.
- """
-
- openid_ = from_openid_response(openid_response) #create janrain OpenID object
- request.session['openid'] = openid_
- try:
- rel = UserAssociation.objects.get(openid_url__exact = str(openid_))
- except:
- # try to register this new user
- return register(request)
- user_ = rel.user
- if user_.is_active:
- user_.backend = "django.contrib.auth.backends.ModelBackend"
- login(request, user_)
-
- return HttpResponseRedirect(get_next_url(request))
-
-def is_association_exist(openid_url):
- """ test if an openid is already in database """
- is_exist = True
- try:
- uassoc = UserAssociation.objects.get(openid_url__exact = openid_url)
- except:
- is_exist = False
- return is_exist
-
-@not_authenticated
-def register(request):
- """
- register an openid.
-
- If user is already a member he can associate its openid with
- its account.
-
- A new account could also be created and automaticaly associated
- to the openid.
-
- url : /complete/
-
- template : authopenid/complete.html
- """
-
- openid_ = request.session.get('openid', None)
- next = get_next_url(request)
- if not openid_:
- return HttpResponseRedirect(reverse('user_signin') + '?next=%s' % next)
-
- nickname = openid_.sreg.get('nickname', '')
- email = openid_.sreg.get('email', '')
- form1 = OpenidRegisterForm(initial={
- 'next': next,
- 'username': nickname,
- 'email': email,
- })
- form2 = OpenidVerifyForm(initial={
- 'next': next,
- 'username': nickname,
- })
- email_feeds_form = EditUserEmailFeedsForm()
-
- user_ = None
- is_redirect = False
- if request.POST:
- if 'bnewaccount' in request.POST.keys():
- form1 = OpenidRegisterForm(request.POST)
- email_feeds_form = EditUserEmailFeedsForm(request.POST)
- if form1.is_valid() and email_feeds_form.is_valid():
- next = form1.cleaned_data['next']
- is_redirect = True
- tmp_pwd = User.objects.make_random_password()
- user_ = User.objects.create_user(form1.cleaned_data['username'],
- form1.cleaned_data['email'], tmp_pwd)
-
- user_.set_unusable_password()
- # make association with openid
- uassoc = UserAssociation(openid_url=str(openid_),
- user_id=user_.id)
- uassoc.save()
-
- # login
- user_.backend = "django.contrib.auth.backends.ModelBackend"
- login(request, user_)
- email_feeds_form.save(user_)
- elif 'bverify' in request.POST.keys():
- form2 = OpenidVerifyForm(request.POST)
- if form2.is_valid():
- is_redirect = True
- next = form2.cleaned_data['next']
- user_ = form2.get_user()
-
- uassoc = UserAssociation(openid_url=str(openid_),
- user_id=user_.id)
- uassoc.save()
- login(request, user_)
-
- #check if we need to post a question that was added anonymously
- #this needs to be a function call becase this is also done
- #if user just logged in and did not need to create the new account
-
- if user_ != None:
- if settings.EMAIL_VALIDATION == 'on':
- send_new_email_key(user_,nomessage=True)
- output = validation_email_sent(request)
- set_email_validation_message(user_) #message set after generating view
- return output
- if user_.is_authenticated():
- return HttpResponseRedirect(reverse('index'))
- else:
- raise Exception('openid login failed')#should not ever get here
-
- openid_str = str(openid_)
- bits = openid_str.split('/')
- base_url = bits[2] #assume this is base url
- url_bits = base_url.split('.')
- provider_name = url_bits[-2].lower()
-
- providers = {'yahoo':'Yahoo!',
- 'flickr':'flickr™',
- 'google':'Google™',
- 'aol':'AOL',
- 'myopenid':'MyOpenID',
- }
- if provider_name not in providers:
- provider_logo = provider_name
- else:
- provider_logo = providers[provider_name]
-
- return render('authopenid/complete.html', {
- 'form1': form1,
- 'form2': form2,
- 'email_feeds_form': email_feeds_form,
- 'provider':mark_safe(provider_logo),
- 'username': nickname,
- 'email': email,
- 'login_type':'openid',
- 'gravatar_faq_url':reverse('faq') + '#gravatar',
- }, context_instance=RequestContext(request))
-
-def signin_failure(request, message):
- """
- falure with openid signin. Go back to signin page.
-
- template : "authopenid/signin.html"
- """
- next = get_next_url(request)
- form_signin = OpenidSigninForm(initial={'next': next})
- form_auth = ClassicLoginForm(initial={'next': next})
-
- return render('authopenid/signin.html', {
- 'msg': message,
- 'form1': form_auth,
- 'form2': form_signin,
- }, context_instance=RequestContext(request))
-
-@not_authenticated
-def signup(request):
- """
- signup page. Create a legacy account
-
- url : /signup/"
-
- templates: authopenid/signup.html, authopenid/confirm_email.txt
- """
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
- next = get_next_url(request)
- if request.POST:
- form = ClassicRegisterForm(request.POST)
- email_feeds_form = EditUserEmailFeedsForm(request.POST)
-
- #validation outside if to remember form values
- form1_is_valid = form.is_valid()
- form2_is_valid = email_feeds_form.is_valid()
- if form1_is_valid and form2_is_valid:
- next = form.cleaned_data['next']
- username = form.cleaned_data['username']
- password = form.cleaned_data['password1']
- email = form.cleaned_data['email']
-
- user_ = User.objects.create_user( username,email,password )
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- external_login.create_user(username,email,password)
-
- user_.backend = "django.contrib.auth.backends.ModelBackend"
- login(request, user_)
- email_feeds_form.save(user_)
-
- # send email
- subject = _("Welcome email subject line")
- message_template = loader.get_template(
- 'authopenid/confirm_email.txt'
- )
- message_context = Context({
- 'signup_url': settings.APP_URL + reverse('user_signin'),
- 'username': username,
- 'password': password,
- })
- message = message_template.render(message_context)
- send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
- [user_.email])
- return HttpResponseRedirect(next)
- else:
- form = ClassicRegisterForm(initial={'next':next})
- email_feeds_form = EditUserEmailFeedsForm()
- return render('authopenid/signup.html', {
- 'form': form,
- 'email_feeds_form': email_feeds_form
- }, context_instance=RequestContext(request))
- #what if request is not posted?
-
-@login_required
-def signout(request):
- """
- signout from the website. Remove openid from session and kill it.
-
- url : /signout/"
- """
- try:
- del request.session['openid']
- except KeyError:
- pass
- logout(request)
- return HttpResponseRedirect(get_next_url(request))
-
-def xrdf(request):
- url_host = get_url_host(request)
- return_to = [
- "%s%s" % (url_host, reverse('user_complete_signin'))
- ]
- return render('authopenid/yadis.xrdf', {
- 'return_to': return_to
- }, context_instance=RequestContext(request))
-
-@login_required
-def account_settings(request):
- """
- index pages to changes some basic account settings :
- - change password
- - change email
- - associate a new openid
- - delete account
-
- url : /
-
- template : authopenid/settings.html
- """
- msg = request.GET.get('msg', '')
- is_openid = True
-
- try:
- uassoc = UserAssociation.objects.get(
- user__username__exact=request.user.username
- )
- except:
- is_openid = False
-
-
- return render('authopenid/settings.html', {
- 'msg': msg,
- 'is_openid': is_openid
- }, context_instance=RequestContext(request))
-
-@login_required
-def changepw(request):
- """
- change password view.
-
- url : /changepw/
- template: authopenid/changepw.html
- """
- user_ = request.user
-
- if user_.has_usable_password():
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
- else:
- 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('authopenid/changepw.html', {'form': form },
- context_instance=RequestContext(request))
-
-def find_email_validation_messages(user):
- msg_text = _('your email needs to be validated see %(details_url)s') \
- % {'details_url':reverse('faq') + '#validate'}
- return user.message_set.filter(message__exact=msg_text)
-
-def set_email_validation_message(user):
- messages = find_email_validation_messages(user)
- msg_text = _('your email needs to be validated see %(details_url)s') \
- % {'details_url':reverse('faq') + '#validate'}
- if len(messages) == 0:
- user.message_set.create(message=msg_text)
-
-def clear_email_validation_message(user):
- messages = find_email_validation_messages(user)
- messages.delete()
-
-def set_new_email(user, new_email, nomessage=False):
- if new_email != user.email:
- user.email = new_email
- user.email_isvalid = False
- user.save()
- if settings.EMAIL_VALIDATION == 'on':
- send_new_email_key(user,nomessage=nomessage)
-
-def _send_email_key(user):
- """private function. sends email containing validation key
- to user's email address
- """
- subject = _("Email verification subject line")
- message_template = loader.get_template('authopenid/email_validation.txt')
- import settings
- message_context = Context({
- 'validation_link': settings.APP_URL + reverse('user_verifyemail', kwargs={'id':user.id,'key':user.email_key})
- })
- message = message_template.render(message_context)
- send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])
-
-def send_new_email_key(user,nomessage=False):
- import random
- random.seed()
- user.email_key = '%032x' % random.getrandbits(128)
- user.save()
- _send_email_key(user)
- if nomessage==False:
- set_email_validation_message(user)
-
-@login_required
-def send_email_key(request):
- """
- url = /email/sendkey/
-
- view that is shown right after sending email key
- email sending is called internally
-
- raises 404 if email validation is off
- if current email is valid shows 'key_not_sent' view of
- authopenid/changeemail.html template
- """
-
- if settings.EMAIL_VALIDATION != 'off':
- if request.user.email_isvalid:
- return render('authopenid/changeemail.html',
- { 'email': request.user.email,
- 'action_type': 'key_not_sent',
- 'change_link': reverse('user_changeemail')},
- context_instance=RequestContext(request)
- )
- else:
- send_new_email_key(request.user)
- return validation_email_sent(request)
- else:
- raise Http404
-
-
-#internal server view used as return value by other views
-def validation_email_sent(request):
- return render('authopenid/changeemail.html',
- { 'email': request.user.email,
- 'change_email_url': reverse('user_changeemail'),
- 'action_type': 'validate', },
- context_instance=RequestContext(request))
-
-def verifyemail(request,id=None,key=None):
- """
- view that is shown when user clicks email validation link
- url = /email/verify/{{user.id}}/{{user.email_key}}/
- """
- if settings.EMAIL_VALIDATION != 'off':
- user = User.objects.get(id=id)
- if user:
- if user.email_key == key:
- user.email_isvalid = True
- clear_email_validation_message(user)
- user.save()
- return render('authopenid/changeemail.html', {
- 'action_type': 'validation_complete',
- }, context_instance=RequestContext(request))
- raise Http404
-
-@login_required
-def changeemail(request, action='change'):
- """
- changeemail view. requires openid with request type GET
-
- url: /email/*
-
- template : authopenid/changeemail.html
- """
- msg = request.GET.get('msg', None)
- extension_args = {}
- user_ = request.user
-
- if request.POST:
- if 'cancel' in request.POST:
- msg = _('your email was not changed')
- request.user.message_set.create(message=msg)
- return HttpResponseRedirect(get_next_url(request))
- form = ChangeEmailForm(request.POST, user=user_)
- if form.is_valid():
- new_email = form.cleaned_data['email']
- if new_email != user_.email:
- if settings.EMAIL_VALIDATION == 'on':
- action = 'validate'
- else:
- action = 'done_novalidate'
- set_new_email(user_, new_email,nomessage=True)
- else:
- action = 'keep'
-
- elif not request.POST and 'openid.mode' in request.GET:
- redirect_to = get_url_host(request) + reverse('user_changeemail')
- return complete(request, emailopenid_success,
- emailopenid_failure, redirect_to)
- else:
- form = ChangeEmailForm(initial={'email': user_.email},
- user=user_)
-
- output = render('authopenid/changeemail.html', {
- 'form': form,
- 'email': user_.email,
- 'action_type': action,
- 'gravatar_faq_url': reverse('faq') + '#gravatar',
- 'change_email_url': reverse('user_changeemail'),
- 'msg': msg
- }, context_instance=RequestContext(request))
-
- if action == 'validate':
- set_email_validation_message(user_)
-
- return output
-
-def emailopenid_success(request, identity_url, openid_response):
- openid_ = from_openid_response(openid_response)
-
- user_ = request.user
- try:
- uassoc = UserAssociation.objects.get(
- openid_url__exact=identity_url
- )
- except:
- return emailopenid_failure(request,
- _("No OpenID %s found associated in our database" % identity_url))
-
- if uassoc.user.username != request.user.username:
- return emailopenid_failure(request,
- _("The OpenID %s isn't associated to current user logged in" %
- identity_url))
-
- new_email = request.session.get('new_email', '')
- if new_email:
- user_.email = new_email
- user_.save()
- del request.session['new_email']
- msg = _("Email Changed.")
-
- redirect = "%s?msg=%s" % (reverse('user_account_settings'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
-
-def emailopenid_failure(request, message):
- redirect_to = "%s?msg=%s" % (
- reverse('user_changeemail'), urlquote_plus(message))
- return HttpResponseRedirect(redirect_to)
-
-@login_required
-def changeopenid(request):
- """
- change openid view. Allow user to change openid
- associated to its username.
-
- url : /changeopenid/
-
- template: authopenid/changeopenid.html
- """
-
- extension_args = {}
- openid_url = ''
- has_openid = True
- msg = request.GET.get('msg', '')
-
- user_ = request.user
-
- try:
- uopenid = UserAssociation.objects.get(user=user_)
- openid_url = uopenid.openid_url
- except:
- has_openid = False
-
- redirect_to = get_url_host(request) + reverse('user_changeopenid')
- if request.POST and has_openid:
- form = ChangeopenidForm(request.POST, user=user_)
- if form.is_valid():
- return ask_openid(request, form.cleaned_data['openid_url'],
- redirect_to, on_failure=changeopenid_failure)
- elif not request.POST and has_openid:
- if 'openid.mode' in request.GET:
- return complete(request, changeopenid_success,
- changeopenid_failure, redirect_to)
-
- form = ChangeopenidForm(initial={'openid_url': openid_url }, user=user_)
- return render('authopenid/changeopenid.html', {
- 'form': form,
- 'has_openid': has_openid,
- 'msg': msg
- }, context_instance=RequestContext(request))
-
-def changeopenid_success(request, identity_url, openid_response):
- openid_ = from_openid_response(openid_response)
- is_exist = True
- try:
- uassoc = UserAssociation.objects.get(openid_url__exact=identity_url)
- except:
- is_exist = False
-
- if not is_exist:
- try:
- uassoc = UserAssociation.objects.get(
- user__username__exact=request.user.username
- )
- uassoc.openid_url = identity_url
- uassoc.save()
- except:
- uassoc = UserAssociation(user=request.user,
- openid_url=identity_url)
- uassoc.save()
- elif uassoc.user.username != request.user.username:
- return changeopenid_failure(request,
- _('This OpenID is already associated with another account.'))
-
- request.session['openids'] = []
- request.session['openids'].append(openid_)
-
- msg = _("OpenID %s is now associated with your account." % identity_url)
- redirect = "%s?msg=%s" % (
- reverse('user_account_settings'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
-
-def changeopenid_failure(request, message):
- redirect_to = "%s?msg=%s" % (
- reverse('user_changeopenid'),
- urlquote_plus(message))
- return HttpResponseRedirect(redirect_to)
-
-@login_required
-def delete(request):
- """
- delete view. Allow user to delete its account. Password/openid are required to
- confirm it. He should also check the confirm checkbox.
-
- url : /delete
-
- template : authopenid/delete.html
- """
-
- extension_args = {}
-
- user_ = request.user
-
- redirect_to = get_url_host(request) + reverse('user_delete')
- if request.POST:
- form = DeleteForm(request.POST, user=user_)
- if form.is_valid():
- if not form.test_openid:
- user_.delete()
- return signout(request)
- else:
- return ask_openid(request, form.cleaned_data['password'],
- redirect_to, on_failure=deleteopenid_failure)
- elif not request.POST and 'openid.mode' in request.GET:
- return complete(request, deleteopenid_success, deleteopenid_failure,
- redirect_to)
-
- form = DeleteForm(user=user_)
-
- msg = request.GET.get('msg','')
- return render('authopenid/delete.html', {
- 'form': form,
- 'msg': msg,
- }, context_instance=RequestContext(request))
-
-def deleteopenid_success(request, identity_url, openid_response):
- openid_ = from_openid_response(openid_response)
-
- user_ = request.user
- try:
- uassoc = UserAssociation.objects.get(
- openid_url__exact=identity_url
- )
- except:
- return deleteopenid_failure(request,
- _("No OpenID %s found associated in our database" % identity_url))
-
- if uassoc.user.username == user_.username:
- user_.delete()
- return signout(request)
- else:
- return deleteopenid_failure(request,
- _("The OpenID %s isn't associated to current user logged in" %
- identity_url))
-
- msg = _("Account deleted.")
- redirect = reverse('index') + u"/?msg=%s" % (urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
-
-def deleteopenid_failure(request, message):
- redirect_to = "%s?msg=%s" % (reverse('user_delete'), urlquote_plus(message))
- return HttpResponseRedirect(redirect_to)
-
-def external_legacy_login_info(request):
- return render('authopenid/external_legacy_login_info.html', context_instance=RequestContext(request))
-
-def sendpw(request):
- """
- send a new password to the user. It return a mail with
- a new pasword and a confirm link in. To activate the
- new password, the user should click on confirm link.
-
- url : /sendpw/
-
- templates : authopenid/sendpw_email.txt, authopenid/sendpw.html
- """
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
-
- msg = request.GET.get('msg','')
- if request.POST:
- form = EmailPasswordForm(request.POST)
- if form.is_valid():
- new_pw = User.objects.make_random_password()
- confirm_key = UserPasswordQueue.objects.get_new_confirm_key()
- try:
- uqueue = UserPasswordQueue.objects.get(
- user=form.user_cache
- )
- except:
- uqueue = UserPasswordQueue(
- user=form.user_cache
- )
- uqueue.new_password = new_pw
- uqueue.confirm_key = confirm_key
- uqueue.save()
- # send email
- subject = _("Request for new password")
- message_template = loader.get_template(
- 'authopenid/sendpw_email.txt')
- key_link = settings.APP_URL + reverse('user_confirmchangepw') + '?key=' + confirm_key
- message_context = Context({
- 'site_url': settings.APP_URL + reverse('index'),
- 'key_link': key_link,
- 'username': form.user_cache.username,
- 'password': new_pw,
- })
- message = message_template.render(message_context)
- send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
- [form.user_cache.email])
- msg = _("A new password and the activation link were sent to your email address.")
- else:
- form = EmailPasswordForm()
-
- return render('authopenid/sendpw.html', {
- 'form': form,
- 'msg': msg
- }, context_instance=RequestContext(request))
-
-
-def confirmchangepw(request):
- """
- view to set new password when the user click on confirm link
- in its mail. Basically it check if the confirm key exist, then
- replace old password with new password and remove confirm
- ley from the queue. Then it redirect the user to signin
- page.
-
- url : /sendpw/confirm/?key
-
- """
- confirm_key = request.GET.get('key', '')
- if not confirm_key:
- return HttpResponseRedirect(reverse('index'))
-
- try:
- uqueue = UserPasswordQueue.objects.get(
- confirm_key__exact=confirm_key
- )
- except:
- msg = _("Could not change password. Confirmation key '%s'\
- is not registered." % confirm_key)
- redirect = "%s?msg=%s" % (
- reverse('user_sendpw'), urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
- try:
- user_ = User.objects.get(id=uqueue.user.id)
- except:
- msg = _("Can not change password. User don't exist anymore \
- in our database.")
- redirect = "%s?msg=%s" % (reverse('user_sendpw'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
- user_.set_password(uqueue.new_password)
- user_.save()
- uqueue.delete()
- msg = _("Password changed for %s. You may now sign in." %
- user_.username)
- redirect = "%s?msg=%s" % (reverse('user_signin'),
- urlquote_plus(msg))
-
- return HttpResponseRedirect(redirect)
+# -*- coding: utf-8 -*-
+# Copyright (c) 2007, 2008, Benoît Chesneau
+# Copyright (c) 2007 Simon Willison, original work on django-openid
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# * notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# * notice, this list of conditions and the following disclaimer in the
+# * documentation and/or other materials provided with the
+# * distribution. Neither the name of the nor the names
+# * of its contributors may be used to endorse or promote products
+# * derived from this software without specific prior written
+# * permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from django.http import HttpResponseRedirect, get_host, Http404, \
+ HttpResponseServerError
+from django.shortcuts import render_to_response as render
+from django.template import RequestContext, loader, Context
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth import authenticate
+from django.core.urlresolvers import reverse
+from django.utils.encoding import smart_unicode
+from django.utils.html import escape
+from django.utils.translation import ugettext as _
+from django.utils.http import urlquote_plus
+from django.utils.safestring import mark_safe
+from django.core.mail import send_mail
+from django.views.defaults import server_error
+
+from openid.consumer.consumer import Consumer, \
+ SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
+from openid.consumer.discover import DiscoveryFailure
+from openid.extensions import sreg
+# needed for some linux distributions like debian
+try:
+ from openid.yadis import xri
+except ImportError:
+ from yadis import xri
+
+import re
+import urllib
+
+
+from forum.forms import EditUserEmailFeedsForm
+from django_authopenid.util import OpenID, DjangoOpenIDStore, from_openid_response, get_next_url
+from django_authopenid.models import UserAssociation, UserPasswordQueue, ExternalLoginData
+from django_authopenid.forms import OpenidSigninForm, ClassicLoginForm, OpenidRegisterForm, \
+ OpenidVerifyForm, ClassicRegisterForm, ChangePasswordForm, ChangeEmailForm, \
+ ChangeopenidForm, DeleteForm, EmailPasswordForm
+import external_login
+import logging
+
+def login(request,user):
+ from django.contrib.auth import login as _login
+ from forum.models import user_logged_in #custom signal
+
+ print 'in login call'
+
+ if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ external_login.login(request,user)
+
+ #1) get old session key
+ session_key = request.session.session_key
+ #2) login and get new session key
+ _login(request,user)
+ #3) send signal with old session key as argument
+ user_logged_in.send(user=user,session_key=session_key,sender=None)
+
+def logout(request):
+ from django.contrib.auth import logout as _logout#for login I've added wrapper below - called login
+ _logout(request)
+ if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ external_login.logout(request)
+
+def get_url_host(request):
+ if request.is_secure():
+ protocol = 'https'
+ else:
+ protocol = 'http'
+ host = escape(get_host(request))
+ return '%s://%s' % (protocol, host)
+
+def get_full_url(request):
+ return get_url_host(request) + request.get_full_path()
+
+def ask_openid(request, openid_url, redirect_to, on_failure=None,
+ sreg_request=None):
+ """ basic function to ask openid and return response """
+ request.encoding = 'UTF-8'
+ on_failure = on_failure or signin_failure
+
+ trust_root = getattr(
+ settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/'
+ )
+ if xri.identifierScheme(openid_url) == 'XRI' and getattr(
+ settings, 'OPENID_DISALLOW_INAMES', False
+ ):
+ msg = _("i-names are not supported")
+ return on_failure(request, msg)
+ consumer = Consumer(request.session, DjangoOpenIDStore())
+ try:
+ auth_request = consumer.begin(openid_url)
+ except DiscoveryFailure:
+ msg = _(u"OpenID %(openid_url)s is invalid" % {'openid_url':openid_url})
+ return on_failure(request, msg)
+
+ if sreg_request:
+ auth_request.addExtension(sreg_request)
+ redirect_url = auth_request.redirectURL(trust_root, redirect_to)
+ return HttpResponseRedirect(redirect_url)
+
+def complete(request, on_success=None, on_failure=None, return_to=None):
+ """ complete openid signin """
+ on_success = on_success or default_on_success
+ on_failure = on_failure or default_on_failure
+
+ consumer = Consumer(request.session, DjangoOpenIDStore())
+ # make sure params are encoded in utf8
+ params = dict((k,smart_unicode(v)) for k, v in request.GET.items())
+ openid_response = consumer.complete(params, return_to)
+
+ if openid_response.status == SUCCESS:
+ return on_success(request, openid_response.identity_url,
+ openid_response)
+ elif openid_response.status == CANCEL:
+ return on_failure(request, 'The request was canceled')
+ elif openid_response.status == FAILURE:
+ return on_failure(request, openid_response.message)
+ elif openid_response.status == SETUP_NEEDED:
+ return on_failure(request, 'Setup needed')
+ else:
+ assert False, "Bad openid status: %s" % openid_response.status
+
+def default_on_success(request, identity_url, openid_response):
+ """ default action on openid signin success """
+ request.session['openid'] = from_openid_response(openid_response)
+ return HttpResponseRedirect(get_next_url(request))
+
+def default_on_failure(request, message):
+ """ default failure action on signin """
+ return render('openid_failure.html', {
+ 'message': message
+ })
+
+
+def not_authenticated(func):
+ """ decorator that redirect user to next page if
+ he is already logged."""
+ def decorated(request, *args, **kwargs):
+ if request.user.is_authenticated():
+ return HttpResponseRedirect(get_next_url(request))
+ return func(request, *args, **kwargs)
+ return decorated
+
+@not_authenticated
+def signin(request,newquestion=False,newanswer=False):
+ """
+ signin page. It manages the legacy authentification (user/password)
+ and openid authentification
+
+ url: /signin/
+
+ template : authopenid/signin.htm
+ """
+ request.encoding = 'UTF-8'
+ on_failure = signin_failure
+ email_feeds_form = EditUserEmailFeedsForm()
+ next = get_next_url(request)
+ form_signin = OpenidSigninForm(initial={'next':next})
+ form_auth = ClassicLoginForm(initial={'next':next})
+
+ if request.POST:
+ #'blogin' - password login
+ if 'blogin' in request.POST.keys():
+ form_auth = ClassicLoginForm(request.POST)
+ if form_auth.is_valid():
+ #have login and password and need to login through external website
+ if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ username = form_auth.cleaned_data['username']
+ password = form_auth.cleaned_data['password']
+ next = form_auth.cleaned_data['next']
+ if form_auth.get_user() == None:
+ #need to create internal user
+
+ #1) save login and password temporarily in session
+ request.session['external_username'] = username
+ request.session['external_password'] = password
+
+ #2) see if username clashes with some existing user
+ #if so, we have to prompt the user to pick a different name
+ username_taken = User.is_username_taken(username)
+ #try:
+ # User.objects.get(username=username)
+ # username_taken = True
+ #except User.DoesNotExist:
+ # username_taken = False
+
+ #3) try to extract user email from external service
+ email = external_login.get_email(username,password)
+
+ email_feeds_form = EditUserEmailFeedsForm()
+ form_data = {'username':username,'email':email,'next':next}
+ form = OpenidRegisterForm(initial=form_data)
+ template_data = {'form1':form,'username':username,\
+ 'email_feeds_form':email_feeds_form,\
+ 'provider':mark_safe(settings.EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME),\
+ 'login_type':'legacy',\
+ 'gravatar_faq_url':reverse('faq') + '#gravatar',\
+ 'external_login_name_is_taken':username_taken}
+ return render('authopenid/complete.html',template_data,\
+ context_instance=RequestContext(request))
+ else:
+ #user existed, external password is ok
+ user = form_auth.get_user()
+ login(request,user)
+ response = HttpResponseRedirect(get_next_url(request))
+ external_login.set_login_cookies(response,user)
+ return response
+ else:
+ #regular password authentication
+ user = form_auth.get_user()
+ login(request, user)
+ return HttpResponseRedirect(get_next_url(request))
+
+ elif 'bnewaccount' in request.POST.keys():
+ #register externally logged in password user with a new local account
+ if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ form = OpenidRegisterForm(request.POST)
+ email_feeds_form = EditUserEmailFeedsForm(request.POST)
+ form1_is_valid = form.is_valid()
+ form2_is_valid = email_feeds_form.is_valid()
+ if form1_is_valid and form2_is_valid:
+ #create the user
+ username = form.cleaned_data['username']
+ password = request.session.get('external_password',None)
+ email = form.cleaned_data['email']
+ print 'got email addr %s' % email
+ if password and username:
+ User.objects.create_user(username,email,password)
+ user = authenticate(username=username,password=password)
+ external_username = request.session['external_username']
+ eld = ExternalLoginData.objects.get(external_username=external_username)
+ eld.user = user
+ eld.save()
+ login(request,user)
+ email_feeds_form.save(user)
+ del request.session['external_username']
+ del request.session['external_password']
+ return HttpResponseRedirect(reverse('index'))
+ else:
+ if password:
+ del request.session['external_username']
+ if username:
+ del request.session['external_password']
+ return HttpResponseServerError()
+ else:
+ username = request.POST.get('username',None)
+ provider = mark_safe(settings.EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME)
+ username_taken = User.is_username_taken(username)
+ data = {'login_type':'legacy','form1':form,'username':username,\
+ 'email_feeds_form':email_feeds_form,'provider':provider,\
+ 'gravatar_faq_url':reverse('faq') + '#gravatar',\
+ 'external_login_name_is_taken':username_taken}
+ return render('authopenid/complete.html',data,
+ context_instance=RequestContext(request))
+ else:
+ raise Http404
+
+ elif 'bsignin' in request.POST.keys() or 'openid_username' in request.POST.keys():
+ form_signin = OpenidSigninForm(request.POST)
+ if form_signin.is_valid():
+ next = form_signin.cleaned_data['next']
+ sreg_req = sreg.SRegRequest(optional=['nickname', 'email'])
+ redirect_to = "%s%s?%s" % (
+ get_url_host(request),
+ reverse('user_complete_signin'),
+ urllib.urlencode({'next':next})
+ )
+ return ask_openid(request,
+ form_signin.cleaned_data['openid_url'],
+ redirect_to,
+ on_failure=signin_failure,
+ sreg_request=sreg_req)
+
+
+ #if request is GET
+ question = None
+ if newquestion == True:
+ from forum.models import AnonymousQuestion as AQ
+ session_key = request.session.session_key
+ qlist = AQ.objects.filter(session_key=session_key).order_by('-added_at')
+ if len(qlist) > 0:
+ question = qlist[0]
+ answer = None
+ if newanswer == True:
+ from forum.models import AnonymousAnswer as AA
+ session_key = request.session.session_key
+ alist = AA.objects.filter(session_key=session_key).order_by('-added_at')
+ if len(alist) > 0:
+ answer = alist[0]
+
+ return render('authopenid/signin.html', {
+ 'question':question,
+ 'answer':answer,
+ 'form1': form_auth,
+ 'form2': form_signin,
+ 'msg': request.GET.get('msg',''),
+ 'sendpw_url': reverse('user_sendpw'),
+ 'fb_api_key': settings.FB_API_KEY,
+ }, context_instance=RequestContext(request))
+
+def complete_signin(request):
+ """ in case of complete signin with openid """
+ return complete(request, signin_success, signin_failure,
+ get_url_host(request) + reverse('user_complete_signin'))
+
+def signin_success(request, identity_url, openid_response):
+ """
+ openid signin success.
+
+ If the openid is already registered, the user is redirected to
+ url set par next or in settings with OPENID_REDIRECT_NEXT variable.
+ If none of these urls are set user is redirectd to /.
+
+ if openid isn't registered user is redirected to register page.
+ """
+
+ openid_ = from_openid_response(openid_response) #create janrain OpenID object
+ request.session['openid'] = openid_
+ try:
+ rel = UserAssociation.objects.get(openid_url__exact = str(openid_))
+ except:
+ # try to register this new user
+ return register(request)
+ user_ = rel.user
+ if user_.is_active:
+ user_.backend = "django.contrib.auth.backends.ModelBackend"
+ login(request, user_)
+
+ return HttpResponseRedirect(get_next_url(request))
+
+def is_association_exist(openid_url):
+ """ test if an openid is already in database """
+ is_exist = True
+ try:
+ uassoc = UserAssociation.objects.get(openid_url__exact = openid_url)
+ except:
+ is_exist = False
+ return is_exist
+
+@not_authenticated
+def register(request):
+ """
+ register an openid.
+
+ If user is already a member he can associate its openid with
+ its account.
+
+ A new account could also be created and automaticaly associated
+ to the openid.
+
+ url : /complete/
+
+ template : authopenid/complete.html
+ """
+
+ openid_ = request.session.get('openid', None)
+ next = get_next_url(request)
+ if not openid_:
+ return HttpResponseRedirect(reverse('user_signin') + '?next=%s' % next)
+
+ nickname = openid_.sreg.get('nickname', '')
+ email = openid_.sreg.get('email', '')
+ form1 = OpenidRegisterForm(initial={
+ 'next': next,
+ 'username': nickname,
+ 'email': email,
+ })
+ form2 = OpenidVerifyForm(initial={
+ 'next': next,
+ 'username': nickname,
+ })
+ email_feeds_form = EditUserEmailFeedsForm()
+
+ user_ = None
+ is_redirect = False
+ if request.POST:
+ if 'bnewaccount' in request.POST.keys():
+ form1 = OpenidRegisterForm(request.POST)
+ email_feeds_form = EditUserEmailFeedsForm(request.POST)
+ if form1.is_valid() and email_feeds_form.is_valid():
+ next = form1.cleaned_data['next']
+ is_redirect = True
+ tmp_pwd = User.objects.make_random_password()
+ user_ = User.objects.create_user(form1.cleaned_data['username'],
+ form1.cleaned_data['email'], tmp_pwd)
+
+ user_.set_unusable_password()
+ # make association with openid
+ uassoc = UserAssociation(openid_url=str(openid_),
+ user_id=user_.id)
+ uassoc.save()
+
+ # login
+ user_.backend = "django.contrib.auth.backends.ModelBackend"
+ login(request, user_)
+ email_feeds_form.save(user_)
+ elif 'bverify' in request.POST.keys():
+ form2 = OpenidVerifyForm(request.POST)
+ if form2.is_valid():
+ is_redirect = True
+ next = form2.cleaned_data['next']
+ user_ = form2.get_user()
+
+ uassoc = UserAssociation(openid_url=str(openid_),
+ user_id=user_.id)
+ uassoc.save()
+ login(request, user_)
+
+ #check if we need to post a question that was added anonymously
+ #this needs to be a function call becase this is also done
+ #if user just logged in and did not need to create the new account
+
+ if user_ != None:
+ if settings.EMAIL_VALIDATION == 'on':
+ send_new_email_key(user_,nomessage=True)
+ output = validation_email_sent(request)
+ set_email_validation_message(user_) #message set after generating view
+ return output
+ if user_.is_authenticated():
+ return HttpResponseRedirect(reverse('index'))
+ else:
+ raise Exception('openid login failed')#should not ever get here
+
+ openid_str = str(openid_)
+ bits = openid_str.split('/')
+ base_url = bits[2] #assume this is base url
+ url_bits = base_url.split('.')
+ provider_name = url_bits[-2].lower()
+
+ providers = {'yahoo':'Yahoo!',
+ 'flickr':'flickr™',
+ 'google':'Google™',
+ 'aol':'AOL',
+ 'myopenid':'MyOpenID',
+ }
+ if provider_name not in providers:
+ provider_logo = provider_name
+ else:
+ provider_logo = providers[provider_name]
+
+ return render('authopenid/complete.html', {
+ 'form1': form1,
+ 'form2': form2,
+ 'email_feeds_form': email_feeds_form,
+ 'provider':mark_safe(provider_logo),
+ 'username': nickname,
+ 'email': email,
+ 'login_type':'openid',
+ 'gravatar_faq_url':reverse('faq') + '#gravatar',
+ }, context_instance=RequestContext(request))
+
+def signin_failure(request, message):
+ """
+ falure with openid signin. Go back to signin page.
+
+ template : "authopenid/signin.html"
+ """
+ next = get_next_url(request)
+ form_signin = OpenidSigninForm(initial={'next': next})
+ form_auth = ClassicLoginForm(initial={'next': next})
+
+ return render('authopenid/signin.html', {
+ 'msg': message,
+ 'form1': form_auth,
+ 'form2': form_signin,
+ }, context_instance=RequestContext(request))
+
+@not_authenticated
+def signup(request):
+ """
+ signup page. Create a legacy account
+
+ url : /signup/"
+
+ templates: authopenid/signup.html, authopenid/confirm_email.txt
+ """
+ if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
+ next = get_next_url(request)
+ if request.POST:
+ form = ClassicRegisterForm(request.POST)
+ email_feeds_form = EditUserEmailFeedsForm(request.POST)
+
+ #validation outside if to remember form values
+ form1_is_valid = form.is_valid()
+ form2_is_valid = email_feeds_form.is_valid()
+ if form1_is_valid and form2_is_valid:
+ next = form.cleaned_data['next']
+ username = form.cleaned_data['username']
+ password = form.cleaned_data['password1']
+ email = form.cleaned_data['email']
+
+ user_ = User.objects.create_user( username,email,password )
+ if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ external_login.create_user(username,email,password)
+
+ user_.backend = "django.contrib.auth.backends.ModelBackend"
+ login(request, user_)
+ email_feeds_form.save(user_)
+
+ # send email
+ subject = _("Welcome email subject line")
+ message_template = loader.get_template(
+ 'authopenid/confirm_email.txt'
+ )
+ message_context = Context({
+ 'signup_url': settings.APP_URL + reverse('user_signin'),
+ 'username': username,
+ 'password': password,
+ })
+ message = message_template.render(message_context)
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
+ [user_.email])
+ return HttpResponseRedirect(next)
+ else:
+ form = ClassicRegisterForm(initial={'next':next})
+ email_feeds_form = EditUserEmailFeedsForm()
+ return render('authopenid/signup.html', {
+ 'form': form,
+ 'email_feeds_form': email_feeds_form
+ }, context_instance=RequestContext(request))
+ #what if request is not posted?
+
+@login_required
+def signout(request):
+ """
+ signout from the website. Remove openid from session and kill it.
+
+ url : /signout/"
+ """
+ try:
+ del request.session['openid']
+ except KeyError:
+ pass
+ logout(request)
+ return HttpResponseRedirect(get_next_url(request))
+
+def xrdf(request):
+ url_host = get_url_host(request)
+ return_to = [
+ "%s%s" % (url_host, reverse('user_complete_signin'))
+ ]
+ return render('authopenid/yadis.xrdf', {
+ 'return_to': return_to
+ }, context_instance=RequestContext(request))
+
+@login_required
+def account_settings(request):
+ """
+ index pages to changes some basic account settings :
+ - change password
+ - change email
+ - associate a new openid
+ - delete account
+
+ url : /
+
+ template : authopenid/settings.html
+ """
+ msg = request.GET.get('msg', '')
+ is_openid = True
+
+ try:
+ uassoc = UserAssociation.objects.get(
+ user__username__exact=request.user.username
+ )
+ except:
+ is_openid = False
+
+
+ return render('authopenid/settings.html', {
+ 'msg': msg,
+ 'is_openid': is_openid
+ }, context_instance=RequestContext(request))
+
+@login_required
+def changepw(request):
+ """
+ change password view.
+
+ url : /changepw/
+ template: authopenid/changepw.html
+ """
+ user_ = request.user
+
+ if user_.has_usable_password():
+ if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
+ else:
+ 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('authopenid/changepw.html', {'form': form },
+ context_instance=RequestContext(request))
+
+def find_email_validation_messages(user):
+ msg_text = _('your email needs to be validated see %(details_url)s') \
+ % {'details_url':reverse('faq') + '#validate'}
+ return user.message_set.filter(message__exact=msg_text)
+
+def set_email_validation_message(user):
+ messages = find_email_validation_messages(user)
+ msg_text = _('your email needs to be validated see %(details_url)s') \
+ % {'details_url':reverse('faq') + '#validate'}
+ if len(messages) == 0:
+ user.message_set.create(message=msg_text)
+
+def clear_email_validation_message(user):
+ messages = find_email_validation_messages(user)
+ messages.delete()
+
+def set_new_email(user, new_email, nomessage=False):
+ if new_email != user.email:
+ user.email = new_email
+ user.email_isvalid = False
+ user.save()
+ if settings.EMAIL_VALIDATION == 'on':
+ send_new_email_key(user,nomessage=nomessage)
+
+def _send_email_key(user):
+ """private function. sends email containing validation key
+ to user's email address
+ """
+ subject = _("Email verification subject line")
+ message_template = loader.get_template('authopenid/email_validation.txt')
+ import settings
+ message_context = Context({
+ 'validation_link': settings.APP_URL + reverse('user_verifyemail', kwargs={'id':user.id,'key':user.email_key})
+ })
+ message = message_template.render(message_context)
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])
+
+def send_new_email_key(user,nomessage=False):
+ import random
+ random.seed()
+ user.email_key = '%032x' % random.getrandbits(128)
+ user.save()
+ _send_email_key(user)
+ if nomessage==False:
+ set_email_validation_message(user)
+
+@login_required
+def send_email_key(request):
+ """
+ url = /email/sendkey/
+
+ view that is shown right after sending email key
+ email sending is called internally
+
+ raises 404 if email validation is off
+ if current email is valid shows 'key_not_sent' view of
+ authopenid/changeemail.html template
+ """
+
+ if settings.EMAIL_VALIDATION != 'off':
+ if request.user.email_isvalid:
+ return render('authopenid/changeemail.html',
+ { 'email': request.user.email,
+ 'action_type': 'key_not_sent',
+ 'change_link': reverse('user_changeemail')},
+ context_instance=RequestContext(request)
+ )
+ else:
+ send_new_email_key(request.user)
+ return validation_email_sent(request)
+ else:
+ raise Http404
+
+
+#internal server view used as return value by other views
+def validation_email_sent(request):
+ return render('authopenid/changeemail.html',
+ { 'email': request.user.email,
+ 'change_email_url': reverse('user_changeemail'),
+ 'action_type': 'validate', },
+ context_instance=RequestContext(request))
+
+def verifyemail(request,id=None,key=None):
+ """
+ view that is shown when user clicks email validation link
+ url = /email/verify/{{user.id}}/{{user.email_key}}/
+ """
+ if settings.EMAIL_VALIDATION != 'off':
+ user = User.objects.get(id=id)
+ if user:
+ if user.email_key == key:
+ user.email_isvalid = True
+ clear_email_validation_message(user)
+ user.save()
+ return render('authopenid/changeemail.html', {
+ 'action_type': 'validation_complete',
+ }, context_instance=RequestContext(request))
+ raise Http404
+
+@login_required
+def changeemail(request, action='change'):
+ """
+ changeemail view. requires openid with request type GET
+
+ url: /email/*
+
+ template : authopenid/changeemail.html
+ """
+ msg = request.GET.get('msg', None)
+ extension_args = {}
+ user_ = request.user
+
+ if request.POST:
+ if 'cancel' in request.POST:
+ msg = _('your email was not changed')
+ request.user.message_set.create(message=msg)
+ return HttpResponseRedirect(get_next_url(request))
+ form = ChangeEmailForm(request.POST, user=user_)
+ if form.is_valid():
+ new_email = form.cleaned_data['email']
+ if new_email != user_.email:
+ if settings.EMAIL_VALIDATION == 'on':
+ action = 'validate'
+ else:
+ action = 'done_novalidate'
+ set_new_email(user_, new_email,nomessage=True)
+ else:
+ action = 'keep'
+
+ elif not request.POST and 'openid.mode' in request.GET:
+ redirect_to = get_url_host(request) + reverse('user_changeemail')
+ return complete(request, emailopenid_success,
+ emailopenid_failure, redirect_to)
+ else:
+ form = ChangeEmailForm(initial={'email': user_.email},
+ user=user_)
+
+ output = render('authopenid/changeemail.html', {
+ 'form': form,
+ 'email': user_.email,
+ 'action_type': action,
+ 'gravatar_faq_url': reverse('faq') + '#gravatar',
+ 'change_email_url': reverse('user_changeemail'),
+ 'msg': msg
+ }, context_instance=RequestContext(request))
+
+ if action == 'validate':
+ set_email_validation_message(user_)
+
+ return output
+
+def emailopenid_success(request, identity_url, openid_response):
+ openid_ = from_openid_response(openid_response)
+
+ user_ = request.user
+ try:
+ uassoc = UserAssociation.objects.get(
+ openid_url__exact=identity_url
+ )
+ except:
+ return emailopenid_failure(request,
+ _("No OpenID %s found associated in our database" % identity_url))
+
+ if uassoc.user.username != request.user.username:
+ return emailopenid_failure(request,
+ _("The OpenID %s isn't associated to current user logged in" %
+ identity_url))
+
+ new_email = request.session.get('new_email', '')
+ if new_email:
+ user_.email = new_email
+ user_.save()
+ del request.session['new_email']
+ msg = _("Email Changed.")
+
+ redirect = "%s?msg=%s" % (reverse('user_account_settings'),
+ urlquote_plus(msg))
+ return HttpResponseRedirect(redirect)
+
+
+def emailopenid_failure(request, message):
+ redirect_to = "%s?msg=%s" % (
+ reverse('user_changeemail'), urlquote_plus(message))
+ return HttpResponseRedirect(redirect_to)
+
+@login_required
+def changeopenid(request):
+ """
+ change openid view. Allow user to change openid
+ associated to its username.
+
+ url : /changeopenid/
+
+ template: authopenid/changeopenid.html
+ """
+
+ extension_args = {}
+ openid_url = ''
+ has_openid = True
+ msg = request.GET.get('msg', '')
+
+ user_ = request.user
+
+ try:
+ uopenid = UserAssociation.objects.get(user=user_)
+ openid_url = uopenid.openid_url
+ except:
+ has_openid = False
+
+ redirect_to = get_url_host(request) + reverse('user_changeopenid')
+ if request.POST and has_openid:
+ form = ChangeopenidForm(request.POST, user=user_)
+ if form.is_valid():
+ return ask_openid(request, form.cleaned_data['openid_url'],
+ redirect_to, on_failure=changeopenid_failure)
+ elif not request.POST and has_openid:
+ if 'openid.mode' in request.GET:
+ return complete(request, changeopenid_success,
+ changeopenid_failure, redirect_to)
+
+ form = ChangeopenidForm(initial={'openid_url': openid_url }, user=user_)
+ return render('authopenid/changeopenid.html', {
+ 'form': form,
+ 'has_openid': has_openid,
+ 'msg': msg
+ }, context_instance=RequestContext(request))
+
+def changeopenid_success(request, identity_url, openid_response):
+ openid_ = from_openid_response(openid_response)
+ is_exist = True
+ try:
+ uassoc = UserAssociation.objects.get(openid_url__exact=identity_url)
+ except:
+ is_exist = False
+
+ if not is_exist:
+ try:
+ uassoc = UserAssociation.objects.get(
+ user__username__exact=request.user.username
+ )
+ uassoc.openid_url = identity_url
+ uassoc.save()
+ except:
+ uassoc = UserAssociation(user=request.user,
+ openid_url=identity_url)
+ uassoc.save()
+ elif uassoc.user.username != request.user.username:
+ return changeopenid_failure(request,
+ _('This OpenID is already associated with another account.'))
+
+ request.session['openids'] = []
+ request.session['openids'].append(openid_)
+
+ msg = _("OpenID %s is now associated with your account." % identity_url)
+ redirect = "%s?msg=%s" % (
+ reverse('user_account_settings'),
+ urlquote_plus(msg))
+ return HttpResponseRedirect(redirect)
+
+
+def changeopenid_failure(request, message):
+ redirect_to = "%s?msg=%s" % (
+ reverse('user_changeopenid'),
+ urlquote_plus(message))
+ return HttpResponseRedirect(redirect_to)
+
+@login_required
+def delete(request):
+ """
+ delete view. Allow user to delete its account. Password/openid are required to
+ confirm it. He should also check the confirm checkbox.
+
+ url : /delete
+
+ template : authopenid/delete.html
+ """
+
+ extension_args = {}
+
+ user_ = request.user
+
+ redirect_to = get_url_host(request) + reverse('user_delete')
+ if request.POST:
+ form = DeleteForm(request.POST, user=user_)
+ if form.is_valid():
+ if not form.test_openid:
+ user_.delete()
+ return signout(request)
+ else:
+ return ask_openid(request, form.cleaned_data['password'],
+ redirect_to, on_failure=deleteopenid_failure)
+ elif not request.POST and 'openid.mode' in request.GET:
+ return complete(request, deleteopenid_success, deleteopenid_failure,
+ redirect_to)
+
+ form = DeleteForm(user=user_)
+
+ msg = request.GET.get('msg','')
+ return render('authopenid/delete.html', {
+ 'form': form,
+ 'msg': msg,
+ }, context_instance=RequestContext(request))
+
+def deleteopenid_success(request, identity_url, openid_response):
+ openid_ = from_openid_response(openid_response)
+
+ user_ = request.user
+ try:
+ uassoc = UserAssociation.objects.get(
+ openid_url__exact=identity_url
+ )
+ except:
+ return deleteopenid_failure(request,
+ _("No OpenID %s found associated in our database" % identity_url))
+
+ if uassoc.user.username == user_.username:
+ user_.delete()
+ return signout(request)
+ else:
+ return deleteopenid_failure(request,
+ _("The OpenID %s isn't associated to current user logged in" %
+ identity_url))
+
+ msg = _("Account deleted.")
+ redirect = reverse('index') + u"/?msg=%s" % (urlquote_plus(msg))
+ return HttpResponseRedirect(redirect)
+
+
+def deleteopenid_failure(request, message):
+ redirect_to = "%s?msg=%s" % (reverse('user_delete'), urlquote_plus(message))
+ return HttpResponseRedirect(redirect_to)
+
+def external_legacy_login_info(request):
+ return render('authopenid/external_legacy_login_info.html', context_instance=RequestContext(request))
+
+def sendpw(request):
+ """
+ send a new password to the user. It return a mail with
+ a new pasword and a confirm link in. To activate the
+ new password, the user should click on confirm link.
+
+ url : /sendpw/
+
+ templates : authopenid/sendpw_email.txt, authopenid/sendpw.html
+ """
+ if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
+
+ msg = request.GET.get('msg','')
+ if request.POST:
+ form = EmailPasswordForm(request.POST)
+ if form.is_valid():
+ new_pw = User.objects.make_random_password()
+ confirm_key = UserPasswordQueue.objects.get_new_confirm_key()
+ try:
+ uqueue = UserPasswordQueue.objects.get(
+ user=form.user_cache
+ )
+ except:
+ uqueue = UserPasswordQueue(
+ user=form.user_cache
+ )
+ uqueue.new_password = new_pw
+ uqueue.confirm_key = confirm_key
+ uqueue.save()
+ # send email
+ subject = _("Request for new password")
+ message_template = loader.get_template(
+ 'authopenid/sendpw_email.txt')
+ key_link = settings.APP_URL + reverse('user_confirmchangepw') + '?key=' + confirm_key
+ message_context = Context({
+ 'site_url': settings.APP_URL + reverse('index'),
+ 'key_link': key_link,
+ 'username': form.user_cache.username,
+ 'password': new_pw,
+ })
+ message = message_template.render(message_context)
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
+ [form.user_cache.email])
+ msg = _("A new password and the activation link were sent to your email address.")
+ else:
+ form = EmailPasswordForm()
+
+ return render('authopenid/sendpw.html', {
+ 'form': form,
+ 'msg': msg
+ }, context_instance=RequestContext(request))
+
+
+def confirmchangepw(request):
+ """
+ view to set new password when the user click on confirm link
+ in its mail. Basically it check if the confirm key exist, then
+ replace old password with new password and remove confirm
+ ley from the queue. Then it redirect the user to signin
+ page.
+
+ url : /sendpw/confirm/?key
+
+ """
+ confirm_key = request.GET.get('key', '')
+ if not confirm_key:
+ return HttpResponseRedirect(reverse('index'))
+
+ try:
+ uqueue = UserPasswordQueue.objects.get(
+ confirm_key__exact=confirm_key
+ )
+ except:
+ msg = _("Could not change password. Confirmation key '%s'\
+ is not registered." % confirm_key)
+ redirect = "%s?msg=%s" % (
+ reverse('user_sendpw'), urlquote_plus(msg))
+ return HttpResponseRedirect(redirect)
+
+ try:
+ user_ = User.objects.get(id=uqueue.user.id)
+ except:
+ msg = _("Can not change password. User don't exist anymore \
+ in our database.")
+ redirect = "%s?msg=%s" % (reverse('user_sendpw'),
+ urlquote_plus(msg))
+ return HttpResponseRedirect(redirect)
+
+ user_.set_password(uqueue.new_password)
+ user_.save()
+ uqueue.delete()
+ msg = _("Password changed for %s. You may now sign in." %
+ user_.username)
+ redirect = "%s?msg=%s" % (reverse('user_signin'),
+ urlquote_plus(msg))
+
+ return HttpResponseRedirect(redirect)
diff --git a/settings_local.py.dist b/settings_local.py.dist
index 14b22e5a..8d645025 100644
--- a/settings_local.py.dist
+++ b/settings_local.py.dist
@@ -1,88 +1,91 @@
-# encoding:utf-8
-import os.path
-from django.utils.translation import ugettext as _
-
-SITE_SRC_ROOT = os.path.dirname(__file__)
-LOG_FILENAME = 'django.osqa.log'
-
-#for logging
-import logging
-logging.basicConfig(filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME), level=logging.DEBUG,)
-
-#ADMINS and MANAGERS
-ADMINS = (('Forum Admin', 'forum@example.com'),)
-MANAGERS = ADMINS
-
-#DEBUG SETTINGS
-DEBUG = False
-TEMPLATE_DEBUG = DEBUG
-INTERNAL_IPS = ('127.0.0.1',)
-
-DATABASE_NAME = 'osqa' # Or path to database file if using sqlite3.
-DATABASE_USER = '' # Not used with sqlite3.
-DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_ENGINE = 'mysql' #mysql, etc
-DATABASE_HOST = ''
-DATABASE_PORT = ''
-
-#Moved from settings.py for better organization. (please check it up to clean up settings.py)
-
-#email server settings
-SERVER_EMAIL = ''
-DEFAULT_FROM_EMAIL = ''
-EMAIL_HOST_USER = ''
-EMAIL_HOST_PASSWORD = ''
-EMAIL_SUBJECT_PREFIX = '[OSQA] '
-EMAIL_HOST='osqa.net'
-EMAIL_PORT='25'
-EMAIL_USE_TLS=False
-
-#LOCALIZATIONS
-TIME_ZONE = 'America/New_York'
-
-###########################
-#
-# this will allow running your forum with url like http://site.com/forum
-#
-# FORUM_SCRIPT_ALIAS = 'forum/'
-#
-FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string
-
-
-#OTHER SETTINGS
-APP_TITLE = u'OSQA: Open Source Q&A Forum'
-APP_SHORT_NAME = u'OSQA'
-APP_KEYWORDS = u'OSQA,CNPROG,forum,community'
-APP_DESCRIPTION = u'Ask and answer questions.'
-APP_INTRO = u'
Ask and answer questions, make the world better!
'
-APP_COPYRIGHT = 'Copyright OSQA, 2009. Some rights reserved under creative commons license.'
-LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
-GREETING_URL = LOGIN_URL #may be url of "faq" page or "about", etc
-
-USE_I18N = True
-LANGUAGE_CODE = 'en'
-EMAIL_VALIDATION = 'off' #string - on|off
-MIN_USERNAME_LENGTH = 1
-EMAIL_UNIQUE = False
-APP_URL = 'http://osqa.net' #used by email notif system and RSS
-GOOGLE_SITEMAP_CODE = ''
-GOOGLE_ANALYTICS_KEY = ''
-BOOKS_ON = False
-WIKI_ON = True
-USE_EXTERNAL_LEGACY_LOGIN = False
-EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.net'
-EXTERNAL_LEGACY_LOGIN_PORT = 80
-EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'OSQA'
-FEEDBACK_SITE_URL = None #None or url
-
-DJANGO_VERSION = 1.1
-RESOURCE_REVISION=4
-
-USE_SPHINX_SEARCH = False #if True all SPHINX_* settings are required
-#also sphinx search engine and djangosphinxs app must be installed
-#sample sphinx configuration file is /sphinx/sphinx.conf
-SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
-SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
-#last item, especially if you have just one :)
-SPHINX_SERVER='localhost'
-SPHINX_PORT=3312
+# encoding:utf-8
+import os.path
+from django.utils.translation import ugettext as _
+
+SITE_SRC_ROOT = os.path.dirname(__file__)
+LOG_FILENAME = 'django.osqa.log'
+
+#for logging
+import logging
+logging.basicConfig(filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME), level=logging.DEBUG,)
+
+#ADMINS and MANAGERS
+ADMINS = (('Forum Admin', 'forum@example.com'),)
+MANAGERS = ADMINS
+
+#DEBUG SETTINGS
+DEBUG = False
+TEMPLATE_DEBUG = DEBUG
+INTERNAL_IPS = ('127.0.0.1',)
+
+DATABASE_NAME = 'osqa' # Or path to database file if using sqlite3.
+DATABASE_USER = '' # Not used with sqlite3.
+DATABASE_PASSWORD = '' # Not used with sqlite3.
+DATABASE_ENGINE = 'mysql' #mysql, etc
+DATABASE_HOST = ''
+DATABASE_PORT = ''
+
+#Moved from settings.py for better organization. (please check it up to clean up settings.py)
+
+#email server settings
+SERVER_EMAIL = ''
+DEFAULT_FROM_EMAIL = ''
+EMAIL_HOST_USER = ''
+EMAIL_HOST_PASSWORD = ''
+EMAIL_SUBJECT_PREFIX = '[OSQA] '
+EMAIL_HOST='osqa.net'
+EMAIL_PORT='25'
+EMAIL_USE_TLS=False
+
+#LOCALIZATIONS
+TIME_ZONE = 'America/New_York'
+
+###########################
+#
+# this will allow running your forum with url like http://site.com/forum
+#
+# FORUM_SCRIPT_ALIAS = 'forum/'
+#
+FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string
+
+
+#OTHER SETTINGS
+APP_TITLE = u'OSQA: Open Source Q&A Forum'
+APP_SHORT_NAME = u'OSQA'
+APP_KEYWORDS = u'OSQA,CNPROG,forum,community'
+APP_DESCRIPTION = u'Ask and answer questions.'
+APP_INTRO = u'
Ask and answer questions, make the world better!
'
+APP_COPYRIGHT = 'Copyright OSQA, 2009. Some rights reserved under creative commons license.'
+LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
+GREETING_URL = LOGIN_URL #may be url of "faq" page or "about", etc
+
+USE_I18N = True
+LANGUAGE_CODE = 'en'
+EMAIL_VALIDATION = 'off' #string - on|off
+MIN_USERNAME_LENGTH = 1
+EMAIL_UNIQUE = False
+APP_URL = 'http://osqa.net' #used by email notif system and RSS
+GOOGLE_SITEMAP_CODE = ''
+GOOGLE_ANALYTICS_KEY = ''
+BOOKS_ON = False
+WIKI_ON = True
+USE_EXTERNAL_LEGACY_LOGIN = False
+EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.net'
+EXTERNAL_LEGACY_LOGIN_PORT = 80
+EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'OSQA'
+FEEDBACK_SITE_URL = None #None or url
+
+DJANGO_VERSION = 1.1
+RESOURCE_REVISION=4
+
+USE_SPHINX_SEARCH = False #if True all SPHINX_* settings are required
+#also sphinx search engine and djangosphinxs app must be installed
+#sample sphinx configuration file is /sphinx/sphinx.conf
+SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
+SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
+#last item, especially if you have just one :)
+SPHINX_SERVER='localhost'
+SPHINX_PORT=3312
+
+#Facebook settings
+FB_API_KEY='' #your api key from facebook
diff --git a/templates/authopenid/signin.html b/templates/authopenid/signin.html
old mode 100644
new mode 100755
index 1363661e..3a12f1c6
--- a/templates/authopenid/signin.html
+++ b/templates/authopenid/signin.html
@@ -1,173 +1,178 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-{% load extra_tags %}
-{% block title %}{% spaceless %}{% trans "User login" %}{% endspaceless %}{% endblock %}
-{% block forejs %}
-
-
-
-
-
-
-{% endblock %}
-{% block content %}
-
- {% trans "User login" %}
-
- {% if msg %}
-
{{ msg }}
- {% endif %}
- {% if answer %}
-
- {% 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 %}
-
- {% endif %}
- {% if question %}
-
- {% blocktrans with question.title as title and question.summary as summary %}Your question
- {{title}} {{summary}} will be posted once you log in
- {% endblocktrans %}
-
+ {% 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 %}
+
+ {% endif %}
+ {% if question %}
+
+ {% blocktrans with question.title as title and question.summary as summary %}Your question
+ {{title}} {{summary}} will be posted once you log in
+ {% endblocktrans %}
+
-{% trans "how to login with password through external login website" %}
+{% blocktrans %}how to login with password through external login website or use {{feedback_url}}{% endblocktrans %}
{% trans "1) Real name - required for the Wiki, but not shown on the forum by default" %}
+
+
+
+
+
+
+
+
+ {{f.first_name}}
+ {% if f.first_name.errors %}
+
{{ f.first_name.errors|join:", " }}
+ {% endif %}
+
+
+ {{f.last_name}}
+ {% if f.last_name.errors %}
+
{{ f.last_name.errors|join:", " }}
+ {% endif %}
+
+
+ {{f.user_title}}
+ {% if f.user_title.errors %}
+
{{ f.user_title.errors|join:", " }}
+ {% endif %}
+
+
+
+
{% trans "2) Forum screen name" %}
+
{% trans "Just skip this to use your full name at the forum, otherwise please check below" %}
+
+ {{f.use_separate_screen_name}}
+
+
+
+ {{f.screen_name}}
+
+ {% if f.screen_name.errors %}
+
{{ f.screen_name.errors|join:", " }}
+ {% endif %}
+
{% trans "Please remember that forum screen name is not your login name. Screen name allows you stay anonymous at the forum - you can change it at any time too. Login name cannot be changed." %}
Your answers will be indispensable.
+Please feel free to ask something too! Hopefully you will like this forum and the wiki and invite your coworkers and friends to join.
+
+
Might you consider sharing some of the digital documentation and pulse sequences that
+perhaps had accumulated in your lab? It's very easy to upload
+files to the wiki as it is to edit the pages directly.
+
+
Best wishes,
+Wiki Server Admin.
+
+
P.S. An email with the confirmation code has been sent to {{wiki_user.user_email}}.
+Please follow the included link to confirm your email address.
+{% if wiki_user.user_title == 'prof' %}
+
+Also, you are always welcome to advertise open positions in your laboratory on the wiki.
+{% endif %}
+
+{% endspaceless %}
diff --git a/templates/mediawiki/welcome_email.txt b/templates/mediawiki/welcome_email.txt
new file mode 100644
index 00000000..c282d9e5
--- /dev/null
+++ b/templates/mediawiki/welcome_email.txt
@@ -0,0 +1,28 @@
+{% spaceless %}
+{% load i18n %}
+{% load smart_if %}
+{% if title == 'prof' %}
+{% blocktrans %}Dear Professor {{last_name}},{% endblocktrans %}
+{% endif %}
+{% if title == 'dr' %}
+{% blocktrans %}Dear Dr. {{last_name}},{% endblocktrans %}
+{% endif %}
+{% if title == 'none' %}
+{% blocktrans %}Dear {{first_name}},{% endblocktrans %}
+{% endif %}
+{% endspaceless %}
+
+{% trans "Thank you for joining OSQA online community!" %}
+
+{% trans "A very brief introduction to OSQA community follows this technical information, included for your record:" %}
+{% blocktrans %}* please visit {{email_confirmation_url}} to confirm your email for the OSQA wiki
+* your OSQA login name is {{login_name}}, email address {{user_email}}.
+* password recovery information can be always found here: {{password_recovery_url}}{% endblocktrans %}
+
+{% trans "A brief introduction to the OSQA online community for the new user." %}
+
+{% blocktrans %}Sincerely,
+Adminstrator of the OSQA website.{% endblocktrans %}
+
+{% blocktrans %}P.S. If you believe that this message was sent in error please tell us
+about it by email at {{admin_email}}.{% endblocktrans %}
diff --git a/templates/mediawiki/welcome_professor_email.txt b/templates/mediawiki/welcome_professor_email.txt
new file mode 100644
index 00000000..6b05889d
--- /dev/null
+++ b/templates/mediawiki/welcome_professor_email.txt
@@ -0,0 +1,19 @@
+{% spaceless %}
+{% load i18n %}
+{% blocktrans %}Dear Professor {{last_name}},{% endblocktrans %}
+{% endspaceless %}
+
+{% trans "Thanks a lot for joining OSQA online community!" %}
+
+{% trans "A very brief introduction to OSQA community follows this technical information, included for your record:" %}
+{% blocktrans %}* please visit {{email_confirmation_url}} to confirm your email for the OSQA wiki
+* your OSQA login name is {{login_name}}, email address {{user_email}}.
+* password recovery information can be always found here: {{password_recovery_url}}{% endblocktrans %}
+
+{% trans "A brief introduction to the OSQA online community for the new professor user." %}
+
+{% blocktrans %}Sincerely,
+Adminstrator of the OSQA website.{% endblocktrans %}
+
+{% blocktrans %}P.S. If you believe that this message was sent in error please tell us
+about it by email at {{admin_email}}.{% endblocktrans %}
diff --git a/templates/notarobot.html b/templates/notarobot.html
new file mode 100644
index 00000000..698c5696
--- /dev/null
+++ b/templates/notarobot.html
@@ -0,0 +1,15 @@
+{% extends "base_content.html" %}
+{% load i18n %}
+{% block title %}{% spaceless %}{% trans "Please prove that you are a Human Being" %}{% endspaceless %}{% endblock %}
+{% block content %}
+{% comment %} this form is set up to be used in wizards {% endcomment %}
+
{% trans "1) Real name - required for the Wiki, but not shown on the forum by default" %}
-
-
-
-
-
-
-
-
- {{f.first_name}}
- {% if f.first_name.errors %}
-
{{ f.first_name.errors|join:", " }}
- {% endif %}
-
-
- {{f.last_name}}
- {% if f.last_name.errors %}
-
{{ f.last_name.errors|join:", " }}
- {% endif %}
-
-
- {{f.user_title}}
- {% if f.user_title.errors %}
-
{{ f.user_title.errors|join:", " }}
- {% endif %}
-
-
-
-
{% trans "2) Forum screen name" %}
-
{% trans "Just skip this to use your full name at the forum, otherwise please check below" %}
-
- {{f.use_separate_screen_name}}
-
-
-
- {{f.screen_name}}
-
- {% if f.screen_name.errors %}
-
{{ f.screen_name.errors|join:", " }}
- {% endif %}
-
{% trans "Please remember that forum screen name is not your login name. Screen name allows you stay anonymous at the forum - you can change it at any time too. Login name cannot be changed." %}
Your answers will be indispensable.
-Please feel free to ask something too! Hopefully you will like this forum and the wiki and invite your coworkers and friends to join.
-
-
Might you consider sharing some of the digital documentation and pulse sequences that
-perhaps had accumulated in your lab? It's very easy to upload
-files to the wiki as it is to edit the pages directly.
-
-
Best wishes,
-Wiki Server Admin.
-
-
P.S. An email with the confirmation code has been sent to {{wiki_user.user_email}}.
-Please follow the included link to confirm your email address.
-{% if wiki_user.user_title == 'prof' %}
-
-Also, you are always welcome to advertise open positions in your laboratory on the wiki.
-{% endif %}
-
-{% endspaceless %}
diff --git a/templates/mediawiki/welcome_email.txt b/templates/mediawiki/welcome_email.txt
deleted file mode 100644
index c282d9e5..00000000
--- a/templates/mediawiki/welcome_email.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-{% spaceless %}
-{% load i18n %}
-{% load smart_if %}
-{% if title == 'prof' %}
-{% blocktrans %}Dear Professor {{last_name}},{% endblocktrans %}
-{% endif %}
-{% if title == 'dr' %}
-{% blocktrans %}Dear Dr. {{last_name}},{% endblocktrans %}
-{% endif %}
-{% if title == 'none' %}
-{% blocktrans %}Dear {{first_name}},{% endblocktrans %}
-{% endif %}
-{% endspaceless %}
-
-{% trans "Thank you for joining OSQA online community!" %}
-
-{% trans "A very brief introduction to OSQA community follows this technical information, included for your record:" %}
-{% blocktrans %}* please visit {{email_confirmation_url}} to confirm your email for the OSQA wiki
-* your OSQA login name is {{login_name}}, email address {{user_email}}.
-* password recovery information can be always found here: {{password_recovery_url}}{% endblocktrans %}
-
-{% trans "A brief introduction to the OSQA online community for the new user." %}
-
-{% blocktrans %}Sincerely,
-Adminstrator of the OSQA website.{% endblocktrans %}
-
-{% blocktrans %}P.S. If you believe that this message was sent in error please tell us
-about it by email at {{admin_email}}.{% endblocktrans %}
diff --git a/templates/mediawiki/welcome_professor_email.txt b/templates/mediawiki/welcome_professor_email.txt
deleted file mode 100644
index 6b05889d..00000000
--- a/templates/mediawiki/welcome_professor_email.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-{% spaceless %}
-{% load i18n %}
-{% blocktrans %}Dear Professor {{last_name}},{% endblocktrans %}
-{% endspaceless %}
-
-{% trans "Thanks a lot for joining OSQA online community!" %}
-
-{% trans "A very brief introduction to OSQA community follows this technical information, included for your record:" %}
-{% blocktrans %}* please visit {{email_confirmation_url}} to confirm your email for the OSQA wiki
-* your OSQA login name is {{login_name}}, email address {{user_email}}.
-* password recovery information can be always found here: {{password_recovery_url}}{% endblocktrans %}
-
-{% trans "A brief introduction to the OSQA online community for the new professor user." %}
-
-{% blocktrans %}Sincerely,
-Adminstrator of the OSQA website.{% endblocktrans %}
-
-{% blocktrans %}P.S. If you believe that this message was sent in error please tell us
-about it by email at {{admin_email}}.{% endblocktrans %}
--
cgit v1.2.3-1-g7c22
From b6cce3357041aa7dfbb59ae12355f7c3ed69f687 Mon Sep 17 00:00:00 2001
From: Evgeny Fadeev
Date: Fri, 5 Feb 2010 19:15:09 -0500
Subject: added logging messages to django_authopenid library for debugging, \
also fixed up format of logging output and added HOW_TO_DEBUG file
---
HOW_TO_DEBUG | 39 ++++++++
django_authopenid/forms.py | 4 +-
django_authopenid/middleware.py | 4 +
django_authopenid/models.py | 4 +
django_authopenid/util.py | 3 +-
django_authopenid/views.py | 209 +++++++++++++++++++++++++++++-----------
settings_local.py.dist | 5 +
7 files changed, 208 insertions(+), 60 deletions(-)
create mode 100644 HOW_TO_DEBUG
diff --git a/HOW_TO_DEBUG b/HOW_TO_DEBUG
new file mode 100644
index 00000000..ba36198a
--- /dev/null
+++ b/HOW_TO_DEBUG
@@ -0,0 +1,39 @@
+1) LOGGING
+Please remember that log files may contain plaintext passwords, etc.
+
+Please do not add print statements - at least do not commit them to git
+because in some environments printing to stdout causes errors
+
+Instead use python logging this way:
+--------------------------------
+#somewere on top of file
+import logging
+
+#anywhere below
+logging.debug('this maybe works')
+logging.error('have big error!')
+#or even
+logging.debug('') #this will add time, line number, function and file record
+#sometimes useful record for call tracing on its own
+#etc - take a look at http://docs.python.org/library/logging.html
+-------------------------------
+
+in OSQA logging is currently set up in settings_local.py.dist
+please update it if you need - in older revs logging strings have less info
+
+messages of interest can be grepped out of the log file by module/file/function name
+e.g. to take out all django_authopenid logs run:
+>grep 'osqa\/django_authopenid' log/django.osqa.log | sed 's/^.*MSG: //'
+in the example above 'sed' call truncates out a long prefix
+and makes output look more meaningful
+
+2) DJANGO DEBUG TOOLBAR
+osqa works with django debug toolbar
+if debugging under apache server, check
+that debug toolbar media is loaded correctly
+if toolbar is enabled but you do not see it, possibly some Alias statement
+in apache config is wrong in your VirtualHost or elsewhere
+
+3) If you discover new debugging techniques, please add here.
+Possible areas to improve - at this point there is no SQL query logging,
+as well as request data and http header.
diff --git a/django_authopenid/forms.py b/django_authopenid/forms.py
index 2fe6db74..5ec21c1c 100644
--- a/django_authopenid/forms.py
+++ b/django_authopenid/forms.py
@@ -94,7 +94,6 @@ class ClassicLoginForm(forms.Form):
self.cleaned_data[field] = value
return value
-
def clean_username(self):
return self._clean_nonempty_field('username')
@@ -103,7 +102,7 @@ class ClassicLoginForm(forms.Form):
def clean(self):
"""
- this clean function actuall cleans username and password
+ this clean function actually cleans username and password
test if password is valid for this username
this is really the "authenticate" function
@@ -118,7 +117,6 @@ class ClassicLoginForm(forms.Form):
self.user_cache = None
if username and password:
-
if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
pw_ok = False
try:
diff --git a/django_authopenid/middleware.py b/django_authopenid/middleware.py
index 2900d54c..2be8da90 100644
--- a/django_authopenid/middleware.py
+++ b/django_authopenid/middleware.py
@@ -2,6 +2,8 @@
from django_authopenid import mimeparse
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
+from django.conf import settings
+import logging
__all__ = ["OpenIDMiddleware"]
@@ -12,6 +14,7 @@ class OpenIDMiddleware(object):
"""
def process_request(self, request):
request.openid = request.session.get('openid', None)
+ logging.debug('openid in session is: %s' % str(request.openid))
def process_response(self, request, response):
if response.status_code != 200 or len(response.content) < 200:
@@ -20,5 +23,6 @@ class OpenIDMiddleware(object):
if path == "/" and request.META.has_key('HTTP_ACCEPT') and \
mimeparse.best_match(['text/html', 'application/xrds+xml'],
request.META['HTTP_ACCEPT']) == 'application/xrds+xml':
+ logging.debug('redirecting to yadis_xrdf:%s' % reverse('yadis_xrdf'))
return HttpResponseRedirect(reverse('yadis_xrdf'))
return response
diff --git a/django_authopenid/models.py b/django_authopenid/models.py
index 7b2e1c02..a12c2fec 100644
--- a/django_authopenid/models.py
+++ b/django_authopenid/models.py
@@ -71,6 +71,10 @@ class UserPasswordQueue(models.Model):
return self.user.username
class ExternalLoginData(models.Model):
+ """this class was added by Evgeny to associate
+ external authentication user with django user
+ probably it just does not belong here... (EF)
+ """
external_username = models.CharField(max_length=40, unique=True, null=False)
external_session_data = models.TextField()
user = models.ForeignKey(User, null=True)
diff --git a/django_authopenid/util.py b/django_authopenid/util.py
index 165756e0..969af0b9 100644
--- a/django_authopenid/util.py
+++ b/django_authopenid/util.py
@@ -14,7 +14,7 @@ try:
except:
from yadis import xri
-import time, base64, hashlib, operator
+import time, base64, hashlib, operator, logging
from utils.forms import clean_next, get_next_url
from models import Association, Nonce
@@ -23,6 +23,7 @@ __all__ = ['OpenID', 'DjangoOpenIDStore', 'from_openid_response', 'clean_next']
class OpenID:
def __init__(self, openid_, issued, attrs=None, sreg_=None):
+ logging.debug('init janrain openid object')
self.openid = openid_
self.issued = issued
self.attrs = attrs or {}
diff --git a/django_authopenid/views.py b/django_authopenid/views.py
index b09dea7c..16a78864 100755
--- a/django_authopenid/views.py
+++ b/django_authopenid/views.py
@@ -60,7 +60,6 @@ except ImportError:
import re
import urllib
-
from forum.forms import SimpleEmailSubscribeForm
from django_authopenid.util import OpenID, DjangoOpenIDStore, from_openid_response
from django_authopenid.models import UserAssociation, UserPasswordQueue, ExternalLoginData
@@ -84,6 +83,7 @@ def login(request,user):
#2) login and get new session key
_login(request,user)
#3) send signal with old session key as argument
+ logging.debug('logged in user %s with session key %s' % (user.username, session_key))
user_logged_in.send(user=user,session_key=session_key,sender=None)
def logout(request):
@@ -116,17 +116,22 @@ def ask_openid(request, openid_url, redirect_to, on_failure=None,
settings, 'OPENID_DISALLOW_INAMES', False
):
msg = _("i-names are not supported")
+ logging.debug('openid failed becaise i-names are not supported')
return on_failure(request, msg)
consumer = Consumer(request.session, DjangoOpenIDStore())
try:
auth_request = consumer.begin(openid_url)
except DiscoveryFailure:
msg = _(u"OpenID %(openid_url)s is invalid" % {'openid_url':openid_url})
+ logging.debug(msg)
return on_failure(request, msg)
+ logging.debug('openid seemed to work')
if sreg_request:
+ logging.debug('adding sreg_request - wtf it is?')
auth_request.addExtension(sreg_request)
redirect_url = auth_request.redirectURL(trust_root, redirect_to)
+ logging.debug('redirecting to %s' % redirect_url)
return HttpResponseRedirect(redirect_url)
def complete(request, on_success=None, on_failure=None, return_to=None):
@@ -134,30 +139,40 @@ def complete(request, on_success=None, on_failure=None, return_to=None):
on_success = on_success or default_on_success
on_failure = on_failure or default_on_failure
+ logging.debug('in django_authopenid.complete')
+
consumer = Consumer(request.session, DjangoOpenIDStore())
# make sure params are encoded in utf8
params = dict((k,smart_unicode(v)) for k, v in request.GET.items())
openid_response = consumer.complete(params, return_to)
if openid_response.status == SUCCESS:
+ logging.debug('SUCCESS')
return on_success(request, openid_response.identity_url,
openid_response)
elif openid_response.status == CANCEL:
+ logging.debug('CANCEL')
return on_failure(request, 'The request was canceled')
elif openid_response.status == FAILURE:
+ logging.debug('FAILURE')
return on_failure(request, openid_response.message)
elif openid_response.status == SETUP_NEEDED:
+ logging.debug('SETUP NEEDED')
return on_failure(request, 'Setup needed')
else:
+ logging.debug('BAD OPENID STATUS')
assert False, "Bad openid status: %s" % openid_response.status
def default_on_success(request, identity_url, openid_response):
""" default action on openid signin success """
+ logging.debug('')
request.session['openid'] = from_openid_response(openid_response)
+ logging.debug('performing default action on openid success %s' % get_next_url(request))
return HttpResponseRedirect(get_next_url(request))
def default_on_failure(request, message):
""" default failure action on signin """
+ logging.debug('default openid failure action')
return render_to_response('openid_failure.html', {
'message': message
})
@@ -177,11 +192,12 @@ def signin(request,newquestion=False,newanswer=False):
"""
signin page. It manages the legacy authentification (user/password)
and openid authentification
-
+
url: /signin/
template : authopenid/signin.htm
"""
+ logging.debug('in signin view')
request.encoding = 'UTF-8'
on_failure = signin_failure
email_feeds_form = SimpleEmailSubscribeForm()
@@ -189,9 +205,10 @@ def signin(request,newquestion=False,newanswer=False):
form_signin = OpenidSigninForm(initial={'next':next})
form_auth = ClassicLoginForm(initial={'next':next})
- if request.POST:
+ if request.method == 'POST':
#'blogin' - password login
if 'blogin' in request.POST.keys():
+ logging.debug('processing classic login form submission')
form_auth = ClassicLoginForm(request.POST)
if form_auth.is_valid():
#have login and password and need to login through external website
@@ -201,19 +218,19 @@ def signin(request,newquestion=False,newanswer=False):
next = form_auth.cleaned_data['next']
if form_auth.get_user() == None:
#need to create internal user
-
+
#1) save login and password temporarily in session
request.session['external_username'] = username
request.session['external_password'] = password
-
+
#2) try to extract user email and nickname from external service
email = EXTERNAL_LOGIN_APP.api.get_email(username,password)
screen_name = EXTERNAL_LOGIN_APP.api.get_screen_name(username,password)
-
+
#3) see if username clashes with some existing user
#if so, we have to prompt the user to pick a different name
username_taken = User.is_username_taken(screen_name)
-
+
email_feeds_form = SimpleEmailSubscribeForm()
form_data = {'username':screen_name,'email':email,'next':next}
form = OpenidRegisterForm(initial=form_data)
@@ -237,8 +254,9 @@ def signin(request,newquestion=False,newanswer=False):
user = form_auth.get_user()
login(request, user)
return HttpResponseRedirect(get_next_url(request))
-
+
elif 'bnewaccount' in request.POST.keys():
+ logging.debug('processing classic (login/password) create account form submission')
#register externally logged in password user with a new local account
if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
form = OpenidRegisterForm(request.POST)
@@ -283,10 +301,12 @@ def signin(request,newquestion=False,newanswer=False):
context_instance=RequestContext(request))
else:
raise Http404
-
+
elif 'bsignin' in request.POST.keys() or 'openid_username' in request.POST.keys():
+ logging.debug('processing signin with openid submission')
form_signin = OpenidSigninForm(request.POST)
if form_signin.is_valid():
+ logging.debug('OpenidSigninForm is valid')
next = form_signin.cleaned_data['next']
sreg_req = sreg.SRegRequest(optional=['nickname', 'email'])
redirect_to = "%s%s?%s" % (
@@ -299,13 +319,17 @@ def signin(request,newquestion=False,newanswer=False):
redirect_to,
on_failure=signin_failure,
sreg_request=sreg_req)
-
-
+ else:
+ logging.debug('OpenidSigninForm is NOT valid! -> redisplay login view')
+
#if request is GET
+ if request.method == 'GET':
+ logging.debug('request method was GET')
question = None
if newquestion == True:
from forum.models import AnonymousQuestion as AQ
session_key = request.session.session_key
+ logging.debug('retrieving anonymously posted question associated with session %s' % session_key)
qlist = AQ.objects.filter(session_key=session_key).order_by('-added_at')
if len(qlist) > 0:
question = qlist[0]
@@ -313,10 +337,12 @@ def signin(request,newquestion=False,newanswer=False):
if newanswer == True:
from forum.models import AnonymousAnswer as AA
session_key = request.session.session_key
+ logging.debug('retrieving posted answer associated with session %s' % session_key)
alist = AA.objects.filter(session_key=session_key).order_by('-added_at')
if len(alist) > 0:
answer = alist[0]
-
+
+ logging.debug('showing signin view')
return render_to_response('authopenid/signin.html', {
'question':question,
'answer':answer,
@@ -329,32 +355,41 @@ def signin(request,newquestion=False,newanswer=False):
def complete_signin(request):
""" in case of complete signin with openid """
+ logging.debug('')#blank log just for the trace
return complete(request, signin_success, signin_failure,
get_url_host(request) + reverse('user_complete_signin'))
def signin_success(request, identity_url, openid_response):
"""
openid signin success.
-
+
If the openid is already registered, the user is redirected to
url set par next or in settings with OPENID_REDIRECT_NEXT variable.
If none of these urls are set user is redirectd to /.
-
+
if openid isn't registered user is redirected to register page.
"""
+ logging.debug('')
openid_ = from_openid_response(openid_response) #create janrain OpenID object
request.session['openid'] = openid_
try:
+ logging.debug('trying to get user associated with this openid...')
rel = UserAssociation.objects.get(openid_url__exact = str(openid_))
+ logging.debug('success')
except:
+ logging.debug('failed --> try to register brand new user')
# try to register this new user
return register(request)
user_ = rel.user
if user_.is_active:
user_.backend = "django.contrib.auth.backends.ModelBackend"
+ logging.debug('user is active --> attached django auth ModelBackend --> calling login')
login(request, user_)
-
+ logging.debug('success')
+ else:
+ logging.debug('user is inactive, do not log them in')
+ logging.debug('redirecting to %s' % get_next_url(request))
return HttpResponseRedirect(get_next_url(request))
def is_association_exist(openid_url):
@@ -364,29 +399,32 @@ def is_association_exist(openid_url):
uassoc = UserAssociation.objects.get(openid_url__exact = openid_url)
except:
is_exist = False
+ logging.debug(str(is_exist))
return is_exist
@not_authenticated
def register(request):
"""
register an openid.
-
+
If user is already a member he can associate its openid with
its account.
-
+
A new account could also be created and automaticaly associated
to the openid.
-
+
url : /complete/
-
+
template : authopenid/complete.html
"""
-
+
+ logging.debug('')
openid_ = request.session.get('openid', None)
next = get_next_url(request)
if not openid_:
+ logging.debug('oops, no openid in session --> go back to signin')
return HttpResponseRedirect(reverse('user_signin') + '?next=%s' % next)
-
+
nickname = openid_.sreg.get('nickname', '')
email = openid_.sreg.get('email', '')
form1 = OpenidRegisterForm(initial={
@@ -399,55 +437,73 @@ def register(request):
'username': nickname,
})
email_feeds_form = SimpleEmailSubscribeForm()
-
+
user_ = None
is_redirect = False
- if request.POST:
+ logging.debug('request method is %s' % request.method)
+ if request.method == 'POST':
if 'bnewaccount' in request.POST.keys():
+ logging.debug('trying to create new account associated with openid')
form1 = OpenidRegisterForm(request.POST)
email_feeds_form = SimpleEmailSubscribeForm(request.POST)
- if form1.is_valid() and email_feeds_form.is_valid():
+ if not form1.is_valid():
+ logging.debug('OpenidRegisterForm is INVALID')
+ elif not email_feeds_form.is_valid():
+ logging.debug('SimpleEmailSubscribeForm is INVALID')
+ else:
+ logging.debug('OpenidRegisterForm and SimpleEmailSubscribeForm are valid')
next = form1.cleaned_data['next']
is_redirect = True
+ logging.debug('creatng new django user %s ...' % form1.cleaned_data['username'])
tmp_pwd = User.objects.make_random_password()
user_ = User.objects.create_user(form1.cleaned_data['username'],
form1.cleaned_data['email'], tmp_pwd)
-
+
user_.set_unusable_password()
# make association with openid
- uassoc = UserAssociation(openid_url=str(openid_),
- user_id=user_.id)
+ logging.debug('creating new openid user association %s <--> %s' \
+ % (user_.username, str(openid_)))
+ uassoc = UserAssociation(openid_url=str(openid_), user_id=user_.id)
uassoc.save()
-
+
# login
user_.backend = "django.contrib.auth.backends.ModelBackend"
+ logging.debug('logging the user in')
login(request, user_)
+ logging.debug('saving email feed settings')
email_feeds_form.save(user_)
elif 'bverify' in request.POST.keys():
+ logging.debug('processing OpenidVerify form')
form2 = OpenidVerifyForm(request.POST)
if form2.is_valid():
+ logging.debug('form is valid')
is_redirect = True
next = form2.cleaned_data['next']
user_ = form2.get_user()
-
+ logging.debug('creating new openid user association %s <--> %s' \
+ % (user_.username, str(openid_)))
uassoc = UserAssociation(openid_url=str(openid_),
user_id=user_.id)
uassoc.save()
+ logging.debug('logging the user in')
login(request, user_)
-
+
#check if we need to post a question that was added anonymously
#this needs to be a function call becase this is also done
#if user just logged in and did not need to create the new account
if user_ != None:
if settings.EMAIL_VALIDATION == 'on':
+ logging.debug('sending email validation')
send_new_email_key(user_,nomessage=True)
output = validation_email_sent(request)
set_email_validation_message(user_) #message set after generating view
return output
if user_.is_authenticated():
+ logging.debug('success, send user to main page')
return HttpResponseRedirect(reverse('index'))
else:
+ logging.debug('have really strange error')
raise Exception('openid login failed')#should not ever get here
openid_str = str(openid_)
@@ -455,7 +511,7 @@ def register(request):
base_url = bits[2] #assume this is base url
url_bits = base_url.split('.')
provider_name = url_bits[-2].lower()
-
+
providers = {'yahoo':'Yahoo!',
'flickr':'flickr™',
'google':'Google™',
@@ -464,9 +520,11 @@ def register(request):
}
if provider_name not in providers:
provider_logo = provider_name
+ logging.error('openid provider named "%s" has no pretty customized logo' % provider_name)
else:
provider_logo = providers[provider_name]
-
+
+ logging.debug('printing authopenid/complete.html output')
return render_to_response('authopenid/complete.html', {
'form1': form1,
'form2': form2,
@@ -484,10 +542,11 @@ def signin_failure(request, message):
template : "authopenid/signin.html"
"""
+ logging.debug('')
next = get_next_url(request)
form_signin = OpenidSigninForm(initial={'next': next})
form_auth = ClassicLoginForm(initial={'next': next})
-
+
return render_to_response('authopenid/signin.html', {
'msg': message,
'form1': form_auth,
@@ -498,18 +557,21 @@ def signin_failure(request, message):
def signup(request):
"""
signup page. Create a legacy account
-
+
url : /signup/"
-
+
templates: authopenid/signup.html, authopenid/confirm_email.txt
"""
+ logging.debug('')
if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ logging.debug('handling external legacy login registration')
return HttpResponseRedirect(reverse('user_external_legacy_login_signup'))
next = get_next_url(request)
- if request.POST:
+ logging.debug('request method was %s' % request.method)
+ if request.method == 'POST':
form = ClassicRegisterForm(request.POST)
email_feeds_form = SimpleEmailSubscribeForm(request.POST)
-
+
#validation outside if to remember form values
form1_is_valid = form.is_valid()
form2_is_valid = email_feeds_form.is_valid()
@@ -518,11 +580,11 @@ def signup(request):
username = form.cleaned_data['username']
password = form.cleaned_data['password1']
email = form.cleaned_data['email']
-
+
user_ = User.objects.create_user( username,email,password )
if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
EXTERNAL_LOGIN_APP.api.create_user(username,email,password)
-
+
user_.backend = "django.contrib.auth.backends.ModelBackend"
login(request, user_)
email_feeds_form.save(user_)
@@ -540,10 +602,14 @@ def signup(request):
message = message_template.render(message_context)
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[user_.email])
+ logging.debug('new user with login and password created!')
return HttpResponseRedirect(next)
+ else:
+ logging.debug('create classic account forms were invalid')
else:
form = ClassicRegisterForm(initial={'next':next})
email_feeds_form = SimpleEmailSubscribeForm()
+ logging.debug('printing legacy signup form')
return render_to_response('authopenid/signup.html', {
'form': form,
'email_feeds_form': email_feeds_form
@@ -557,15 +623,20 @@ def signout(request):
url : /signout/"
"""
+ logging.debug('')
try:
+ logging.debug('deleting openid session var')
del request.session['openid']
except KeyError:
+ logging.debug('failed')
pass
logout(request)
+ logging.debug('user logged out')
return HttpResponseRedirect(get_next_url(request))
def xrdf(request):
url_host = get_url_host(request)
+ logging.debug('what does this do??')
return_to = [
"%s%s" % (url_host, reverse('user_complete_signin'))
]
@@ -586,6 +657,7 @@ def account_settings(request):
template : authopenid/settings.html
"""
+ logging.debug('')
msg = request.GET.get('msg', '')
is_openid = True
@@ -610,6 +682,7 @@ def changepw(request):
url : /changepw/
template: authopenid/changepw.html
"""
+ logging.debug('')
user_ = request.user
if user_.has_usable_password():
@@ -710,6 +783,7 @@ def send_email_key(request):
#internal server view used as return value by other views
def validation_email_sent(request):
+ logging.debug('')
return render_to_response('authopenid/changeemail.html',
{ 'email': request.user.email,
'change_email_url': reverse('user_changeemail'),
@@ -721,6 +795,7 @@ def verifyemail(request,id=None,key=None):
view that is shown when user clicks email validation link
url = /email/verify/{{user.id}}/{{user.email_key}}/
"""
+ logging.debug('')
if settings.EMAIL_VALIDATION != 'off':
user = User.objects.get(id=id)
if user:
@@ -731,6 +806,8 @@ def verifyemail(request,id=None,key=None):
return render_to_response('authopenid/changeemail.html', {
'action_type': 'validation_complete',
}, context_instance=RequestContext(request))
+ else:
+ logging.error('hmm, no user found for email validation message - foul play?')
raise Http404
@login_required
@@ -742,6 +819,7 @@ def changeemail(request, action='change'):
template : authopenid/changeemail.html
"""
+ logging.debug('')
msg = request.GET.get('msg', None)
extension_args = {}
user_ = request.user
@@ -786,6 +864,7 @@ def changeemail(request, action='change'):
return output
def emailopenid_success(request, identity_url, openid_response):
+ logging.debug('')
openid_ = from_openid_response(openid_response)
user_ = request.user
@@ -815,6 +894,7 @@ def emailopenid_success(request, identity_url, openid_response):
def emailopenid_failure(request, message):
+ logging.debug('')
redirect_to = "%s?msg=%s" % (
reverse('user_changeemail'), urlquote_plus(message))
return HttpResponseRedirect(redirect_to)
@@ -829,6 +909,7 @@ def changeopenid(request):
template: authopenid/changeopenid.html
"""
+ logging.error('change openid view - never tested it yet!!!')
extension_args = {}
openid_url = ''
@@ -862,6 +943,7 @@ def changeopenid(request):
}, context_instance=RequestContext(request))
def changeopenid_success(request, identity_url, openid_response):
+ logging.error('never tested this worflow')
openid_ = from_openid_response(openid_response)
is_exist = True
try:
@@ -895,6 +977,7 @@ def changeopenid_success(request, identity_url, openid_response):
def changeopenid_failure(request, message):
+ logging.error('never tested this workflow')
redirect_to = "%s?msg=%s" % (
reverse('user_changeopenid'),
urlquote_plus(message))
@@ -905,16 +988,17 @@ def delete(request):
"""
delete view. Allow user to delete its account. Password/openid are required to
confirm it. He should also check the confirm checkbox.
-
+
url : /delete
-
+
template : authopenid/delete.html
"""
-
+ logging.error('deleting account - never tested this')
+
extension_args = {}
user_ = request.user
-
+
redirect_to = get_url_host(request) + reverse('user_delete')
if request.POST:
form = DeleteForm(request.POST, user=user_)
@@ -930,7 +1014,7 @@ def delete(request):
redirect_to)
form = DeleteForm(user=user_)
-
+
msg = request.GET.get('msg','')
return render_to_response('authopenid/delete.html', {
'form': form,
@@ -938,6 +1022,7 @@ def delete(request):
}, context_instance=RequestContext(request))
def deleteopenid_success(request, identity_url, openid_response):
+ logging.error('never tested this')
openid_ = from_openid_response(openid_response)
user_ = request.user
@@ -963,10 +1048,12 @@ def deleteopenid_success(request, identity_url, openid_response):
def deleteopenid_failure(request, message):
+ logging.error('never tested this')
redirect_to = "%s?msg=%s" % (reverse('user_delete'), urlquote_plus(message))
return HttpResponseRedirect(redirect_to)
def external_legacy_login_info(request):
+ logging.debug('maybe this view does not belong in this library')
feedback_url = reverse('feedback')
return render_to_response('authopenid/external_legacy_login_info.html',
{'feedback_url':feedback_url},
@@ -977,18 +1064,22 @@ def sendpw(request):
send a new password to the user. It return a mail with
a new pasword and a confirm link in. To activate the
new password, the user should click on confirm link.
-
+
url : /sendpw/
-
+
templates : authopenid/sendpw_email.txt, authopenid/sendpw.html
"""
+ logging.debug('')
if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
+ logging.debug('delegating to view dealing with external password recovery')
return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
-
+
msg = request.GET.get('msg','')
- if request.POST:
+ logging.debug('request method is %s' % request.method)
+ if request.method == 'POST':
form = EmailPasswordForm(request.POST)
if form.is_valid():
+ logging.debug('EmailPasswordForm is valid')
new_pw = User.objects.make_random_password()
confirm_key = UserPasswordQueue.objects.get_new_confirm_key()
try:
@@ -1007,6 +1098,7 @@ def sendpw(request):
message_template = loader.get_template(
'authopenid/sendpw_email.txt')
key_link = settings.APP_URL + reverse('user_confirmchangepw') + '?key=' + confirm_key
+ logging.debug('emailing new password for %s' % form.user_cache.username)
message_context = Context({
'site_url': settings.APP_URL + reverse('index'),
'key_link': key_link,
@@ -1019,13 +1111,13 @@ def sendpw(request):
msg = _("A new password and the activation link were sent to your email address.")
else:
form = EmailPasswordForm()
-
+
+ logging.debug('showing reset password form')
return render_to_response('authopenid/sendpw.html', {
'form': form,
'msg': msg
}, context_instance=RequestContext(request))
-
def confirmchangepw(request):
"""
view to set new password when the user click on confirm link
@@ -1033,14 +1125,16 @@ def confirmchangepw(request):
replace old password with new password and remove confirm
ley from the queue. Then it redirect the user to signin
page.
-
+
url : /sendpw/confirm/?key
-
+
"""
+ logging.debug('')
confirm_key = request.GET.get('key', '')
if not confirm_key:
+ logging.error('someone called confirm password without a key!')
return HttpResponseRedirect(reverse('index'))
-
+
try:
uqueue = UserPasswordQueue.objects.get(
confirm_key__exact=confirm_key
@@ -1048,25 +1142,28 @@ def confirmchangepw(request):
except:
msg = _("Could not change password. Confirmation key '%s'\
is not registered." % confirm_key)
+ logging.error(msg)
redirect = "%s?msg=%s" % (
reverse('user_sendpw'), urlquote_plus(msg))
return HttpResponseRedirect(redirect)
-
+
try:
user_ = User.objects.get(id=uqueue.user.id)
except:
msg = _("Can not change password. User don't exist anymore \
in our database.")
+ logging.error(msg)
redirect = "%s?msg=%s" % (reverse('user_sendpw'),
urlquote_plus(msg))
return HttpResponseRedirect(redirect)
-
+
user_.set_password(uqueue.new_password)
user_.save()
uqueue.delete()
msg = _("Password changed for %s. You may now sign in." %
user_.username)
+ logging.debug(msg)
redirect = "%s?msg=%s" % (reverse('user_signin'),
urlquote_plus(msg))
-
+
return HttpResponseRedirect(redirect)
diff --git a/settings_local.py.dist b/settings_local.py.dist
index 7fbc0729..0ef11eca 100755
--- a/settings_local.py.dist
+++ b/settings_local.py.dist
@@ -15,6 +15,11 @@ LOG_FILENAME = 'django.osqa.log'
#for logging
import logging
logging.basicConfig(filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME), level=logging.DEBUG,)
+logging.basicConfig(
+ filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME),
+ level=logging.DEBUG,
+ format='%(pathname)s TIME: %(asctime)s MSG: %(filename)s:%(funcName)s:%(lineno)d %(message)s',
+)
#ADMINS and MANAGERS
ADMINS = (('Forum Admin', 'forum@example.com'),)
--
cgit v1.2.3-1-g7c22
From 3e7c42f81352ad73a1395ed0601f2d4fdf9b5359 Mon Sep 17 00:00:00 2001
From: Evgeny Fadeev
Date: Fri, 5 Feb 2010 19:18:17 -0500
Subject: tiny fix to settings_local.py.dist
---
settings_local.py.dist | 1 -
1 file changed, 1 deletion(-)
diff --git a/settings_local.py.dist b/settings_local.py.dist
index 0ef11eca..c38e3dd8 100755
--- a/settings_local.py.dist
+++ b/settings_local.py.dist
@@ -14,7 +14,6 @@ LOG_FILENAME = 'django.osqa.log'
#for logging
import logging
-logging.basicConfig(filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME), level=logging.DEBUG,)
logging.basicConfig(
filename=os.path.join(SITE_SRC_ROOT, 'log', LOG_FILENAME),
level=logging.DEBUG,
--
cgit v1.2.3-1-g7c22
- {{comment.comment}} - - {{comment.user}} - {% spaceless %} - ({% diff_date comment.added_at %}) - {% if request.user|can_delete_comment:comment %} - - {% endif %} - {% endspaceless %} -
- {% endfor %} -