summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhrcerqueira <hrcerqueira@gmail.com>2010-03-01 17:37:32 +0000
committerhrcerqueira <hrcerqueira@gmail.com>2010-03-01 17:37:32 +0000
commitd01a3ff31d96aaedd9ce8ba38cb5d0cde53584fc (patch)
treebc5cd0632fdb2edbde77a5ea0db2be4cdeaedc59
parent0d29fc79deba22027187ae4627a7c38b3fdef2e4 (diff)
downloadaskbot-d01a3ff31d96aaedd9ce8ba38cb5d0cde53584fc.tar.gz
askbot-d01a3ff31d96aaedd9ce8ba38cb5d0cde53584fc.tar.bz2
askbot-d01a3ff31d96aaedd9ce8ba38cb5d0cde53584fc.zip
New auth system, see the wiki for details.
-rwxr-xr-x[-rw-r--r--]forum/__init__.py0
-rwxr-xr-x[-rw-r--r--]forum/admin.py0
-rwxr-xr-x[-rw-r--r--]forum/auth.py0
-rwxr-xr-xforum/authentication/__init__.py27
-rwxr-xr-xforum/authentication/base.py40
-rwxr-xr-xforum/authentication/forms.py31
-rwxr-xr-x[-rw-r--r--]forum/const.py0
-rwxr-xr-x[-rw-r--r--]forum/feed.py0
-rwxr-xr-x[-rw-r--r--]forum/forms.py43
-rwxr-xr-x[-rw-r--r--]forum/management/__init__.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/__init__.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/base_command.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/clean_award_badges.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/message_to_everyone.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/multi_award_badges.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/once_award_badges.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/sample_command.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/send_email_alerts.py0
-rwxr-xr-x[-rw-r--r--]forum/management/commands/subscribe_everyone.py0
-rwxr-xr-x[-rw-r--r--]forum/middleware/__init__.py0
-rwxr-xr-x[-rw-r--r--]forum/middleware/anon_user.py3
-rwxr-xr-x[-rw-r--r--]forum/middleware/cancel.py0
-rwxr-xr-x[-rw-r--r--]forum/middleware/pagesize.py0
-rwxr-xr-xforum/models/__init__.py4
-rwxr-xr-xforum/models/answer.py3
-rwxr-xr-xforum/models/question.py3
-rwxr-xr-xforum/models/user.py10
-rwxr-xr-xforum/modules.py29
-rwxr-xr-x[-rw-r--r--]forum/sitemap.py0
-rwxr-xr-x[-rw-r--r--]forum/skins/README0
-rwxr-xr-x[-rw-r--r--]forum/skins/__init__.py0
-rwxr-xr-x[-rw-r--r--]forum/skins/common/media/README0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/blue-up-arrow-h18px.pngbin593 -> 593 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/box-arrow.gifbin69 -> 69 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/bullet_green.gifbin64 -> 64 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/cc-88x31.pngbin5460 -> 5460 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/cc-wiki.pngbin2333 -> 2333 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/close-small-dark.pngbin226 -> 226 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/close-small-hover.pngbin337 -> 337 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/close-small.pngbin293 -> 293 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/dash.gifbin44 -> 44 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/djangomade124x25_grey.gifbin2035 -> 2035 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/dot-g.gifbin61 -> 61 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/dot-list.gifbin56 -> 56 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/edit.pngbin758 -> 758 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/expander-arrow-hide.gifbin126 -> 126 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/expander-arrow-show.gifbin135 -> 135 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/favicon.gifbin3918 -> 3918 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/feed-icon-small.pngbin689 -> 689 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/gray-up-arrow-h18px.pngbin383 -> 383 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/grippie.pngbin162 -> 162 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/indicator.gifbin2545 -> 2545 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/logo.gifbin2114 -> 2114 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/logo.pngbin2081 -> 2081 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/logo1.pngbin2752 -> 2752 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/logo2.pngbin2124 -> 2124 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/medala.gifbin801 -> 801 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/medala_on.gifbin957 -> 957 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/new.gifbin635 -> 635 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/nophoto.pngbin696 -> 696 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid.gifbin910 -> 910 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/aol.gifbin2205 -> 2205 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/blogger.icobin3638 -> 3638 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/claimid.icobin3638 -> 3638 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/facebook.gifbin2075 -> 2075 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/flickr.icobin1150 -> 1150 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/google.gifbin1596 -> 1596 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/livejournal.icobin5222 -> 5222 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/myopenid.icobin2862 -> 2862 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/openid-inputicon.gifbin237 -> 237 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/openid.gifbin740 -> 740 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/technorati.icobin2294 -> 2294 bytes
-rwxr-xr-xforum/skins/default/media/images/openid/twitter.pngbin0 -> 3130 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/verisign.icobin4710 -> 4710 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/vidoop.icobin1406 -> 1406 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/wordpress.icobin1150 -> 1150 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/openid/yahoo.gifbin1682 -> 1510 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/quest-bg.gifbin294 -> 294 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/vote-accepted-on.pngbin1124 -> 1124 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/vote-accepted.pngbin1058 -> 1058 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/vote-arrow-down-on.pngbin905 -> 905 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/vote-arrow-down.pngbin876 -> 876 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/vote-arrow-up-on.pngbin906 -> 906 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/vote-arrow-up.pngbin843 -> 843 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/vote-favorite-off.pngbin930 -> 930 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/images/vote-favorite-on.pngbin1023 -> 1023 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/aol.gifbin2205 -> 2205 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/blogger-1.pngbin432 -> 432 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/blogger.icobin3638 -> 3638 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/claimid-0.pngbin629 -> 629 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/claimid.icobin3638 -> 3638 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/facebook.gifbin2075 -> 2075 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/flickr.icobin1150 -> 1150 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/flickr.pngbin426 -> 426 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/google.gifbin1596 -> 1596 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/livejournal-1.pngbin713 -> 713 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/livejournal.icobin5222 -> 5222 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/myopenid-2.pngbin511 -> 511 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/myopenid.icobin2862 -> 2862 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/openid-inputicon.gifbin237 -> 237 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/openid.gifbin740 -> 740 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/openidico.pngbin654 -> 654 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/openidico16.pngbin554 -> 554 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/technorati-1.pngbin606 -> 606 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/technorati.icobin2294 -> 2294 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/verisign-2.pngbin859 -> 859 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/verisign.icobin4710 -> 4710 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/vidoop.icobin1406 -> 1406 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/vidoop.pngbin499 -> 499 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/wordpress.icobin1150 -> 1150 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/wordpress.pngbin566 -> 566 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/images/yahoo.gifbin1682 -> 1682 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/jquery.openid.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/jquery-openid/openid.css0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/com.cnprog.admin.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/com.cnprog.editor.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/com.cnprog.i18n.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/com.cnprog.post.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/com.cnprog.tag_selector.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/com.cnprog.utils.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/compress.bat0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/excanvas.pack.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/flot-build.bat0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery-1.2.6.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery-1.2.6.min.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery.ajaxfileupload.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery.flot.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery.flot.pack.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery.form.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery.i18n.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery.openid.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/jquery.validate.pack.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/se_hilite.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/se_hilite_src.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/wmd/images/wmd-buttons.pngbin7465 -> 7465 bytes
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/wmd/showdown-min.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/wmd/showdown.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/wmd/wmd-min.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/wmd/wmd-test.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/wmd/wmd.css0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/wmd/wmd.js0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/js/yuicompressor-2.4.2.jarbin851219 -> 851219 bytes
-rwxr-xr-xforum/skins/default/media/style/auth.css48
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/style/default.css0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/style/jquery.autocomplete.css0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/style/openid.css0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/style/prettify.css0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/media/style/style.css0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/404.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/500.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/about.html2
-rwxr-xr-xforum/skins/default/templates/account_settings.html45
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/answer_edit.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/answer_edit_tips.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/ask.html0
-rwxr-xr-xforum/skins/default/templates/auth/complete.html95
-rwxr-xr-xforum/skins/default/templates/auth/signin.html161
-rwxr-xr-xforum/skins/default/templates/auth/signup.html32
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/badge.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/badges.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/base_content.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/book.html0
-rwxr-xr-xforum/skins/default/templates/changepw.html18
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/close.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/edit_user_email_feeds_form.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/faq.html4
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/feedback.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/feedback_email.txt0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/feeds/rss_description.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/feeds/rss_title.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/footer.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/header.html2
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/logout.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/notarobot.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/pagesize.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/paginator.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/post_contributor_info.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/privacy.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/question.html2
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/question_edit.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/question_edit_tips.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/question_retag.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/question_summary_list_roll.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/questions.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/reopen.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/revisions_answer.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/revisions_question.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/tag_selector.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/tags.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_edit.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_email_subscriptions.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_favorites.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_footer.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_info.html4
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_recent.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_reputation.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_responses.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_stats.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_tabs.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/user_votes.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/users.html0
-rwxr-xr-x[-rw-r--r--]forum/skins/default/templates/users_questions.html0
-rwxr-xr-x[-rw-r--r--]forum/templatetags/__init__.py0
-rwxr-xr-x[-rw-r--r--]forum/templatetags/extra_filters.py0
-rwxr-xr-x[-rw-r--r--]forum/templatetags/extra_tags.py0
-rwxr-xr-x[-rw-r--r--]forum/templatetags/smart_if.py0
-rwxr-xr-x[-rw-r--r--]forum/upfiles/README0
-rwxr-xr-x[-rw-r--r--]forum/urls.py18
-rwxr-xr-x[-rw-r--r--]forum/user_messages/__init__.py0
-rwxr-xr-x[-rw-r--r--]forum/user_messages/context_processors.py0
-rwxr-xr-x[-rw-r--r--]forum/utils/__init__.py0
-rwxr-xr-x[-rw-r--r--]forum/utils/cache.py0
-rwxr-xr-x[-rw-r--r--]forum/utils/decorators.py0
-rwxr-xr-x[-rw-r--r--]forum/utils/diff.py0
-rwxr-xr-x[-rw-r--r--]forum/utils/forms.py0
-rwxr-xr-x[-rw-r--r--]forum/utils/html.py0
-rwxr-xr-x[-rw-r--r--]forum/utils/lists.py0
-rwxr-xr-x[-rw-r--r--]forum/utils/odict.py0
-rwxr-xr-x[-rw-r--r--]forum/views/README2
-rwxr-xr-x[-rw-r--r--]forum/views/__init__.py1
-rwxr-xr-xforum/views/auth.py212
-rwxr-xr-x[-rw-r--r--]forum/views/commands.py0
-rwxr-xr-x[-rw-r--r--]forum/views/meta.py4
-rwxr-xr-x[-rw-r--r--]forum/views/readers.py0
-rwxr-xr-x[-rw-r--r--]forum/views/users.py68
-rwxr-xr-x[-rw-r--r--]forum/views/writers.py4
-rwxr-xr-xforum_modules/facebookauth/__init__.py0
-rwxr-xr-xforum_modules/facebookauth/authentication.py85
-rwxr-xr-xforum_modules/facebookauth/settings.py3
-rwxr-xr-xforum_modules/facebookauth/templates/button.html38
-rwxr-xr-xforum_modules/facebookauth/templates/xd_receiver.html1
-rwxr-xr-xforum_modules/facebookauth/urls.py9
-rwxr-xr-xforum_modules/facebookauth/views.py11
-rwxr-xr-xforum_modules/localauth/__init__.py0
-rwxr-xr-xforum_modules/localauth/authentication.py18
-rwxr-xr-xforum_modules/localauth/forms.py77
-rwxr-xr-xforum_modules/localauth/templates/loginform.html31
-rwxr-xr-xforum_modules/localauth/urls.py8
-rwxr-xr-xforum_modules/localauth/views.py30
-rwxr-xr-xforum_modules/oauthauth/__init__.py0
-rwxr-xr-xforum_modules/oauthauth/authentication.py41
-rwxr-xr-xforum_modules/oauthauth/consumer.py87
-rwxr-xr-xforum_modules/oauthauth/lib/__init__.py0
-rwxr-xr-xforum_modules/oauthauth/lib/oauth.py594
-rwxr-xr-xforum_modules/oauthauth/settings.py3
-rwxr-xr-xforum_modules/openidauth/__init__.py0
-rwxr-xr-xforum_modules/openidauth/authentication.py196
-rwxr-xr-xforum_modules/openidauth/consumer.py112
-rwxr-xr-xforum_modules/openidauth/models.py26
-rwxr-xr-xforum_modules/openidauth/settings.py9
-rwxr-xr-xforum_modules/openidauth/store.py79
-rwxr-xr-xforum_modules/openidauth/templates/openidurl.html20
-rwxr-xr-xforum_modules/pgfulltext/DISABLED0
254 files changed, 2352 insertions, 41 deletions
diff --git a/forum/__init__.py b/forum/__init__.py
index 85cd5d26..85cd5d26 100644..100755
--- a/forum/__init__.py
+++ b/forum/__init__.py
diff --git a/forum/admin.py b/forum/admin.py
index 88643b92..88643b92 100644..100755
--- a/forum/admin.py
+++ b/forum/admin.py
diff --git a/forum/auth.py b/forum/auth.py
index 3533b9ce..3533b9ce 100644..100755
--- a/forum/auth.py
+++ b/forum/auth.py
diff --git a/forum/authentication/__init__.py b/forum/authentication/__init__.py
new file mode 100755
index 00000000..e83ba872
--- /dev/null
+++ b/forum/authentication/__init__.py
@@ -0,0 +1,27 @@
+import re
+from forum.modules import get_modules_script_classes
+from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext
+
+class ConsumerAndContext():
+ def __init__(self, id, consumer, context):
+ self.id = id
+ self.consumer = consumer()
+
+ context.id = id
+ self.context = context
+
+consumers = dict([
+ (re.sub('AuthConsumer$', '', name).lower(), cls) for name, cls
+ in get_modules_script_classes('authentication', AuthenticationConsumer).items()
+ if not re.search('AbstractAuthConsumer$', name)
+ ])
+
+contexts = dict([
+ (re.sub('AuthContext$', '', name).lower(), cls) for name, cls
+ in get_modules_script_classes('authentication', ConsumerTemplateContext).items()
+ ])
+
+AUTH_PROVIDERS = dict([
+ (name, ConsumerAndContext(name, consumers[name], contexts[name])) for name in consumers.keys()
+ if name in contexts
+ ]) \ No newline at end of file
diff --git a/forum/authentication/base.py b/forum/authentication/base.py
new file mode 100755
index 00000000..995f7c96
--- /dev/null
+++ b/forum/authentication/base.py
@@ -0,0 +1,40 @@
+
+class AuthenticationConsumer(object):
+
+ def prepare_authentication_request(self, request, redirect_to):
+ raise NotImplementedError()
+
+ def process_authentication_request(self, response):
+ raise NotImplementedError()
+
+ def get_user_data(self, key):
+ raise NotImplementedError()
+
+
+class ConsumerTemplateContext(object):
+ """
+ Class that provides information about a certain authentication provider context in the signin page.
+
+ class attributes:
+
+ mode - one of BIGICON, SMALLICON, FORM
+
+ human_name - the human readable name of the provider
+
+ extra_js - some providers require us to load extra javascript on the signin page for them to work,
+ this is the place to add those files in the form of a list
+
+ extra_css - same as extra_js but for css files
+ """
+ mode = ''
+ weight = 500
+ human_name = ''
+ extra_js = []
+ extra_css = []
+ show_to_logged_in_user = True
+
+class InvalidAuthentication(Exception):
+ def __init__(self, message):
+ self.message = message
+
+ \ No newline at end of file
diff --git a/forum/authentication/forms.py b/forum/authentication/forms.py
new file mode 100755
index 00000000..04841341
--- /dev/null
+++ b/forum/authentication/forms.py
@@ -0,0 +1,31 @@
+from forum.utils.forms import NextUrlField, UserNameField, UserEmailField
+from forum.models import EmailFeedSetting, Question
+from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext as _
+from django import forms
+from forum.forms import EditUserEmailFeedsForm
+import logging
+
+class SimpleRegistrationForm(forms.Form):
+ next = NextUrlField()
+ username = UserNameField()
+ email = UserEmailField()
+
+
+class SimpleEmailSubscribeForm(forms.Form):
+ SIMPLE_SUBSCRIBE_CHOICES = (
+ ('y',_('okay, let\'s try!')),
+ ('n',_('no OSQA community email please, thanks'))
+ )
+ subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect(), \
+ error_messages={'required':_('please choose one of the options above')},
+ choices=SIMPLE_SUBSCRIBE_CHOICES)
+
+ def save(self,user=None):
+ EFF = EditUserEmailFeedsForm
+ if self.cleaned_data['subscribe'] == 'y':
+ email_settings_form = EFF()
+ logging.debug('%s wants to subscribe' % user.username)
+ else:
+ email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
+ email_settings_form.save(user,save_unbound=True)
diff --git a/forum/const.py b/forum/const.py
index 76fd4a24..76fd4a24 100644..100755
--- a/forum/const.py
+++ b/forum/const.py
diff --git a/forum/feed.py b/forum/feed.py
index e4b929e9..e4b929e9 100644..100755
--- a/forum/feed.py
+++ b/forum/feed.py
diff --git a/forum/forms.py b/forum/forms.py
index f22763f7..c157aa44 100644..100755
--- a/forum/forms.py
+++ b/forum/forms.py
@@ -5,7 +5,9 @@ from models import *
from const import *
from django.utils.translation import ugettext as _
from django.contrib.auth.models import User
-from forum.utils.forms import NextUrlField, UserNameField
+from django.contrib.contenttypes.models import ContentType
+from django.utils.safestring import mark_safe
+from forum.utils.forms import NextUrlField, UserNameField, SetPasswordForm
from recaptcha_django import ReCaptchaField
from django.conf import settings
import logging
@@ -259,6 +261,25 @@ class TagFilterSelectionForm(forms.ModelForm):
return True
return False
+
+class ChangePasswordForm(SetPasswordForm):
+ """ change password form """
+ oldpw = forms.CharField(widget=forms.PasswordInput(attrs={'class':'required'}),
+ label=mark_safe(_('Current password')))
+
+ def __init__(self, data=None, user=None, *args, **kwargs):
+ if user is None:
+ raise TypeError("Keyword argument 'user' must be supplied")
+ super(ChangePasswordForm, self).__init__(data, *args, **kwargs)
+ self.user = user
+
+ def clean_oldpw(self):
+ """ test old password """
+ if not self.user.check_password(self.cleaned_data['oldpw']):
+ raise forms.ValidationError(_("Old password is incorrect. \
+ Please enter the correct password."))
+ return self.cleaned_data['oldpw']
+
class EditUserEmailFeedsForm(forms.Form):
WN = (('w',_('weekly')),('n',_('no email')))
DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
@@ -290,7 +311,7 @@ class EditUserEmailFeedsForm(forms.Form):
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)
+ settings = EmailFeedSetting.objects.filter(subscriber=user)
initial_values = {}
for setting in settings:
feed_type = setting.feed_type
@@ -336,21 +357,3 @@ class EditUserEmailFeedsForm(forms.Form):
user.followed_questions.clear()
return changed
-
-class SimpleEmailSubscribeForm(forms.Form):
- SIMPLE_SUBSCRIBE_CHOICES = (
- ('y',_('okay, let\'s try!')),
- ('n',_('no OSQA community email please, thanks'))
- )
- subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect(), \
- error_messages={'required':_('please choose one of the options above')},
- choices=SIMPLE_SUBSCRIBE_CHOICES)
-
- def save(self,user=None):
- EFF = EditUserEmailFeedsForm
- if self.cleaned_data['subscribe'] == 'y':
- email_settings_form = EFF()
- logging.debug('%s wants to subscribe' % user.username)
- else:
- email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
- email_settings_form.save(user,save_unbound=True)
diff --git a/forum/management/__init__.py b/forum/management/__init__.py
index 8266592a..8266592a 100644..100755
--- a/forum/management/__init__.py
+++ b/forum/management/__init__.py
diff --git a/forum/management/commands/__init__.py b/forum/management/commands/__init__.py
index e69de29b..e69de29b 100644..100755
--- a/forum/management/commands/__init__.py
+++ b/forum/management/commands/__init__.py
diff --git a/forum/management/commands/base_command.py b/forum/management/commands/base_command.py
index c073bf7a..c073bf7a 100644..100755
--- a/forum/management/commands/base_command.py
+++ b/forum/management/commands/base_command.py
diff --git a/forum/management/commands/clean_award_badges.py b/forum/management/commands/clean_award_badges.py
index 117e3a5f..117e3a5f 100644..100755
--- a/forum/management/commands/clean_award_badges.py
+++ b/forum/management/commands/clean_award_badges.py
diff --git a/forum/management/commands/message_to_everyone.py b/forum/management/commands/message_to_everyone.py
index c020c178..c020c178 100644..100755
--- a/forum/management/commands/message_to_everyone.py
+++ b/forum/management/commands/message_to_everyone.py
diff --git a/forum/management/commands/multi_award_badges.py b/forum/management/commands/multi_award_badges.py
index 6b330cf9..6b330cf9 100644..100755
--- a/forum/management/commands/multi_award_badges.py
+++ b/forum/management/commands/multi_award_badges.py
diff --git a/forum/management/commands/once_award_badges.py b/forum/management/commands/once_award_badges.py
index 8c913348..8c913348 100644..100755
--- a/forum/management/commands/once_award_badges.py
+++ b/forum/management/commands/once_award_badges.py
diff --git a/forum/management/commands/sample_command.py b/forum/management/commands/sample_command.py
index 55e67235..55e67235 100644..100755
--- a/forum/management/commands/sample_command.py
+++ b/forum/management/commands/sample_command.py
diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py
index 26eb7790..26eb7790 100644..100755
--- a/forum/management/commands/send_email_alerts.py
+++ b/forum/management/commands/send_email_alerts.py
diff --git a/forum/management/commands/subscribe_everyone.py b/forum/management/commands/subscribe_everyone.py
index c79528f3..c79528f3 100644..100755
--- a/forum/management/commands/subscribe_everyone.py
+++ b/forum/management/commands/subscribe_everyone.py
diff --git a/forum/middleware/__init__.py b/forum/middleware/__init__.py
index e69de29b..e69de29b 100644..100755
--- a/forum/middleware/__init__.py
+++ b/forum/middleware/__init__.py
diff --git a/forum/middleware/anon_user.py b/forum/middleware/anon_user.py
index e05e6f33..866734da 100644..100755
--- a/forum/middleware/anon_user.py
+++ b/forum/middleware/anon_user.py
@@ -3,6 +3,7 @@ from forum.utils.forms import get_next_url
from django.utils.translation import ugettext as _
from forum.user_messages import create_message, get_and_delete_messages
from django.conf import settings
+from django.core.urlresolvers import reverse
import logging
class AnonymousMessageManager(object):
@@ -30,5 +31,5 @@ class ConnectToSessionMessagesMiddleware(object):
#also set the first greeting one time per session only
if 'greeting_set' not in request.session:
request.session['greeting_set'] = True
- msg = _('first time greeting with %(url)s') % {'url':settings.GREETING_URL}
+ msg = _('First time here? Check out the <a href="%s">FAQ</a>!') % reverse('faq')
request.user.message_set.create(message=msg)
diff --git a/forum/middleware/cancel.py b/forum/middleware/cancel.py
index 15a4371d..15a4371d 100644..100755
--- a/forum/middleware/cancel.py
+++ b/forum/middleware/cancel.py
diff --git a/forum/middleware/pagesize.py b/forum/middleware/pagesize.py
index f6e6fcfd..f6e6fcfd 100644..100755
--- a/forum/middleware/pagesize.py
+++ b/forum/middleware/pagesize.py
diff --git a/forum/models/__init__.py b/forum/models/__init__.py
index ccc53a7c..12a02395 100755
--- a/forum/models/__init__.py
+++ b/forum/models/__init__.py
@@ -2,7 +2,7 @@ from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion
from answer import Answer, AnonymousAnswer, AnswerRevision
from tag import Tag, MarkedTag
from meta import Vote, Comment, FlaggedItem
-from user import Activity, AnonymousEmail, EmailFeedSetting
+from user import Activity, AnonymousEmail, EmailFeedSetting, AuthKeyUserAssociation
from repute import Badge, Award, Repute
from base import *
@@ -303,6 +303,7 @@ Repute = Repute
Activity = Activity
EmailFeedSetting = EmailFeedSetting
AnonymousEmail = AnonymousEmail
+AuthKeyUserAssociation = AuthKeyUserAssociation
__all__ = [
'Question',
@@ -328,6 +329,7 @@ __all__ = [
'Activity',
'EmailFeedSetting',
'AnonymousEmail',
+ 'AuthKeyUserAssociation',
'User'
]
diff --git a/forum/models/answer.py b/forum/models/answer.py
index a1580828..14199de3 100755
--- a/forum/models/answer.py
+++ b/forum/models/answer.py
@@ -3,7 +3,8 @@ from base import *
from question import Question
class AnswerManager(models.Manager):
- def create_new(self, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False):
+ @staticmethod
+ def create_new(cls, question=None, author=None, added_at=None, wiki=False, text='', email_notify=False):
answer = Answer(
question = question,
author = author,
diff --git a/forum/models/question.py b/forum/models/question.py
index 27d6e9d9..f916e656 100755
--- a/forum/models/question.py
+++ b/forum/models/question.py
@@ -2,7 +2,8 @@ from base import *
from tag import Tag
class QuestionManager(models.Manager):
- def create_new(self, title=None,author=None,added_at=None, wiki=False,tagnames=None,summary=None, text=None):
+ @staticmethod
+ def create_new(cls, title=None,author=None,added_at=None, wiki=False,tagnames=None,summary=None, text=None):
question = Question(
title = title,
author = author,
diff --git a/forum/models/user.py b/forum/models/user.py
index 4e1a376d..95024162 100755
--- a/forum/models/user.py
+++ b/forum/models/user.py
@@ -65,3 +65,13 @@ class AnonymousEmail(models.Model):
app_label = 'forum'
+class AuthKeyUserAssociation(models.Model):
+ key = models.CharField(max_length=256,null=False,unique=True)
+ provider = models.CharField(max_length=64)
+ user = models.ForeignKey(User)
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+
+ class Meta:
+ app_label = 'forum'
+
+ \ No newline at end of file
diff --git a/forum/modules.py b/forum/modules.py
index 53cc619a..9c072330 100755
--- a/forum/modules.py
+++ b/forum/modules.py
@@ -1,5 +1,8 @@
import os
import types
+import re
+
+from django.template import Template, TemplateDoesNotExist
MODULES_PACKAGE = 'forum_modules'
@@ -19,7 +22,8 @@ def get_modules_script(script_name):
for m in MODULE_LIST:
try:
all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__]))
- except:
+ except Exception, e:
+ #print script_name + ":" + str(e)
pass
return all
@@ -51,4 +55,25 @@ def get_all_handlers(name):
def get_handler(name, default):
all = get_all_handlers(name)
print(len(all))
- return len(all) and all[0] or default \ No newline at end of file
+ return len(all) and all[0] or default
+
+module_template_re = re.compile('^modules\/(\w+)\/(.*)$')
+
+def module_templates_loader(name, dirs=None):
+ result = module_template_re.search(name)
+
+ if result is not None:
+ file_name = os.path.join(MODULES_FOLDER, result.group(1), 'templates', result.group(2))
+
+ if os.path.exists(file_name):
+ try:
+ f = open(file_name, 'r')
+ source = f.read()
+ f.close()
+ return (source, file_name)
+ except:
+ pass
+
+ raise TemplateDoesNotExist, name
+
+module_templates_loader.is_usable = True \ No newline at end of file
diff --git a/forum/sitemap.py b/forum/sitemap.py
index c0c60b5e..c0c60b5e 100644..100755
--- a/forum/sitemap.py
+++ b/forum/sitemap.py
diff --git a/forum/skins/README b/forum/skins/README
index 5565fa83..5565fa83 100644..100755
--- a/forum/skins/README
+++ b/forum/skins/README
diff --git a/forum/skins/__init__.py b/forum/skins/__init__.py
index be6bd4f3..be6bd4f3 100644..100755
--- a/forum/skins/__init__.py
+++ b/forum/skins/__init__.py
diff --git a/forum/skins/common/media/README b/forum/skins/common/media/README
index 3376e754..3376e754 100644..100755
--- a/forum/skins/common/media/README
+++ b/forum/skins/common/media/README
diff --git a/forum/skins/default/media/images/blue-up-arrow-h18px.png b/forum/skins/default/media/images/blue-up-arrow-h18px.png
index e1f29e86..e1f29e86 100644..100755
--- a/forum/skins/default/media/images/blue-up-arrow-h18px.png
+++ b/forum/skins/default/media/images/blue-up-arrow-h18px.png
Binary files differ
diff --git a/forum/skins/default/media/images/box-arrow.gif b/forum/skins/default/media/images/box-arrow.gif
index 89dcf5b3..89dcf5b3 100644..100755
--- a/forum/skins/default/media/images/box-arrow.gif
+++ b/forum/skins/default/media/images/box-arrow.gif
Binary files differ
diff --git a/forum/skins/default/media/images/bullet_green.gif b/forum/skins/default/media/images/bullet_green.gif
index fa530910..fa530910 100644..100755
--- a/forum/skins/default/media/images/bullet_green.gif
+++ b/forum/skins/default/media/images/bullet_green.gif
Binary files differ
diff --git a/forum/skins/default/media/images/cc-88x31.png b/forum/skins/default/media/images/cc-88x31.png
index 0f2a0f10..0f2a0f10 100644..100755
--- a/forum/skins/default/media/images/cc-88x31.png
+++ b/forum/skins/default/media/images/cc-88x31.png
Binary files differ
diff --git a/forum/skins/default/media/images/cc-wiki.png b/forum/skins/default/media/images/cc-wiki.png
index 3e680538..3e680538 100644..100755
--- a/forum/skins/default/media/images/cc-wiki.png
+++ b/forum/skins/default/media/images/cc-wiki.png
Binary files differ
diff --git a/forum/skins/default/media/images/close-small-dark.png b/forum/skins/default/media/images/close-small-dark.png
index 280c1fc7..280c1fc7 100644..100755
--- a/forum/skins/default/media/images/close-small-dark.png
+++ b/forum/skins/default/media/images/close-small-dark.png
Binary files differ
diff --git a/forum/skins/default/media/images/close-small-hover.png b/forum/skins/default/media/images/close-small-hover.png
index 7899aec7..7899aec7 100644..100755
--- a/forum/skins/default/media/images/close-small-hover.png
+++ b/forum/skins/default/media/images/close-small-hover.png
Binary files differ
diff --git a/forum/skins/default/media/images/close-small.png b/forum/skins/default/media/images/close-small.png
index 5a99d31f..5a99d31f 100644..100755
--- a/forum/skins/default/media/images/close-small.png
+++ b/forum/skins/default/media/images/close-small.png
Binary files differ
diff --git a/forum/skins/default/media/images/dash.gif b/forum/skins/default/media/images/dash.gif
index d1ddc507..d1ddc507 100644..100755
--- a/forum/skins/default/media/images/dash.gif
+++ b/forum/skins/default/media/images/dash.gif
Binary files differ
diff --git a/forum/skins/default/media/images/djangomade124x25_grey.gif b/forum/skins/default/media/images/djangomade124x25_grey.gif
index d34bb311..d34bb311 100644..100755
--- a/forum/skins/default/media/images/djangomade124x25_grey.gif
+++ b/forum/skins/default/media/images/djangomade124x25_grey.gif
Binary files differ
diff --git a/forum/skins/default/media/images/dot-g.gif b/forum/skins/default/media/images/dot-g.gif
index 5d6bb28e..5d6bb28e 100644..100755
--- a/forum/skins/default/media/images/dot-g.gif
+++ b/forum/skins/default/media/images/dot-g.gif
Binary files differ
diff --git a/forum/skins/default/media/images/dot-list.gif b/forum/skins/default/media/images/dot-list.gif
index f6a6b865..f6a6b865 100644..100755
--- a/forum/skins/default/media/images/dot-list.gif
+++ b/forum/skins/default/media/images/dot-list.gif
Binary files differ
diff --git a/forum/skins/default/media/images/edit.png b/forum/skins/default/media/images/edit.png
index dcb09be0..dcb09be0 100644..100755
--- a/forum/skins/default/media/images/edit.png
+++ b/forum/skins/default/media/images/edit.png
Binary files differ
diff --git a/forum/skins/default/media/images/expander-arrow-hide.gif b/forum/skins/default/media/images/expander-arrow-hide.gif
index feb6a618..feb6a618 100644..100755
--- a/forum/skins/default/media/images/expander-arrow-hide.gif
+++ b/forum/skins/default/media/images/expander-arrow-hide.gif
Binary files differ
diff --git a/forum/skins/default/media/images/expander-arrow-show.gif b/forum/skins/default/media/images/expander-arrow-show.gif
index 6825c56e..6825c56e 100644..100755
--- a/forum/skins/default/media/images/expander-arrow-show.gif
+++ b/forum/skins/default/media/images/expander-arrow-show.gif
Binary files differ
diff --git a/forum/skins/default/media/images/favicon.gif b/forum/skins/default/media/images/favicon.gif
index 910c2666..910c2666 100644..100755
--- a/forum/skins/default/media/images/favicon.gif
+++ b/forum/skins/default/media/images/favicon.gif
Binary files differ
diff --git a/forum/skins/default/media/images/feed-icon-small.png b/forum/skins/default/media/images/feed-icon-small.png
index b3c949d2..b3c949d2 100644..100755
--- a/forum/skins/default/media/images/feed-icon-small.png
+++ b/forum/skins/default/media/images/feed-icon-small.png
Binary files differ
diff --git a/forum/skins/default/media/images/gray-up-arrow-h18px.png b/forum/skins/default/media/images/gray-up-arrow-h18px.png
index 78767445..78767445 100644..100755
--- a/forum/skins/default/media/images/gray-up-arrow-h18px.png
+++ b/forum/skins/default/media/images/gray-up-arrow-h18px.png
Binary files differ
diff --git a/forum/skins/default/media/images/grippie.png b/forum/skins/default/media/images/grippie.png
index 6524d416..6524d416 100644..100755
--- a/forum/skins/default/media/images/grippie.png
+++ b/forum/skins/default/media/images/grippie.png
Binary files differ
diff --git a/forum/skins/default/media/images/indicator.gif b/forum/skins/default/media/images/indicator.gif
index 1c72ebb5..1c72ebb5 100644..100755
--- a/forum/skins/default/media/images/indicator.gif
+++ b/forum/skins/default/media/images/indicator.gif
Binary files differ
diff --git a/forum/skins/default/media/images/logo.gif b/forum/skins/default/media/images/logo.gif
index ab690de2..ab690de2 100644..100755
--- a/forum/skins/default/media/images/logo.gif
+++ b/forum/skins/default/media/images/logo.gif
Binary files differ
diff --git a/forum/skins/default/media/images/logo.png b/forum/skins/default/media/images/logo.png
index 6a250e35..6a250e35 100644..100755
--- a/forum/skins/default/media/images/logo.png
+++ b/forum/skins/default/media/images/logo.png
Binary files differ
diff --git a/forum/skins/default/media/images/logo1.png b/forum/skins/default/media/images/logo1.png
index d79a6271..d79a6271 100644..100755
--- a/forum/skins/default/media/images/logo1.png
+++ b/forum/skins/default/media/images/logo1.png
Binary files differ
diff --git a/forum/skins/default/media/images/logo2.png b/forum/skins/default/media/images/logo2.png
index bd3cccd9..bd3cccd9 100644..100755
--- a/forum/skins/default/media/images/logo2.png
+++ b/forum/skins/default/media/images/logo2.png
Binary files differ
diff --git a/forum/skins/default/media/images/medala.gif b/forum/skins/default/media/images/medala.gif
index 93dd1a39..93dd1a39 100644..100755
--- a/forum/skins/default/media/images/medala.gif
+++ b/forum/skins/default/media/images/medala.gif
Binary files differ
diff --git a/forum/skins/default/media/images/medala_on.gif b/forum/skins/default/media/images/medala_on.gif
index a18f9e85..a18f9e85 100644..100755
--- a/forum/skins/default/media/images/medala_on.gif
+++ b/forum/skins/default/media/images/medala_on.gif
Binary files differ
diff --git a/forum/skins/default/media/images/new.gif b/forum/skins/default/media/images/new.gif
index 8a220b53..8a220b53 100644..100755
--- a/forum/skins/default/media/images/new.gif
+++ b/forum/skins/default/media/images/new.gif
Binary files differ
diff --git a/forum/skins/default/media/images/nophoto.png b/forum/skins/default/media/images/nophoto.png
index 2daf0ffd..2daf0ffd 100644..100755
--- a/forum/skins/default/media/images/nophoto.png
+++ b/forum/skins/default/media/images/nophoto.png
Binary files differ
diff --git a/forum/skins/default/media/images/openid.gif b/forum/skins/default/media/images/openid.gif
index 8540e12b..8540e12b 100644..100755
--- a/forum/skins/default/media/images/openid.gif
+++ b/forum/skins/default/media/images/openid.gif
Binary files differ
diff --git a/forum/skins/default/media/images/openid/aol.gif b/forum/skins/default/media/images/openid/aol.gif
index decc4f12..decc4f12 100644..100755
--- a/forum/skins/default/media/images/openid/aol.gif
+++ b/forum/skins/default/media/images/openid/aol.gif
Binary files differ
diff --git a/forum/skins/default/media/images/openid/blogger.ico b/forum/skins/default/media/images/openid/blogger.ico
index 1b9730b0..1b9730b0 100644..100755
--- a/forum/skins/default/media/images/openid/blogger.ico
+++ b/forum/skins/default/media/images/openid/blogger.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/claimid.ico b/forum/skins/default/media/images/openid/claimid.ico
index 2b80f491..2b80f491 100644..100755
--- a/forum/skins/default/media/images/openid/claimid.ico
+++ b/forum/skins/default/media/images/openid/claimid.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/facebook.gif b/forum/skins/default/media/images/openid/facebook.gif
index b997b358..b997b358 100644..100755
--- a/forum/skins/default/media/images/openid/facebook.gif
+++ b/forum/skins/default/media/images/openid/facebook.gif
Binary files differ
diff --git a/forum/skins/default/media/images/openid/flickr.ico b/forum/skins/default/media/images/openid/flickr.ico
index 11f6e07f..11f6e07f 100644..100755
--- a/forum/skins/default/media/images/openid/flickr.ico
+++ b/forum/skins/default/media/images/openid/flickr.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/google.gif b/forum/skins/default/media/images/openid/google.gif
index 1b6cd07b..1b6cd07b 100644..100755
--- a/forum/skins/default/media/images/openid/google.gif
+++ b/forum/skins/default/media/images/openid/google.gif
Binary files differ
diff --git a/forum/skins/default/media/images/openid/livejournal.ico b/forum/skins/default/media/images/openid/livejournal.ico
index f3d21ec5..f3d21ec5 100644..100755
--- a/forum/skins/default/media/images/openid/livejournal.ico
+++ b/forum/skins/default/media/images/openid/livejournal.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/myopenid.ico b/forum/skins/default/media/images/openid/myopenid.ico
index ceb06e6a..ceb06e6a 100644..100755
--- a/forum/skins/default/media/images/openid/myopenid.ico
+++ b/forum/skins/default/media/images/openid/myopenid.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/openid-inputicon.gif b/forum/skins/default/media/images/openid/openid-inputicon.gif
index cde836c8..cde836c8 100644..100755
--- a/forum/skins/default/media/images/openid/openid-inputicon.gif
+++ b/forum/skins/default/media/images/openid/openid-inputicon.gif
Binary files differ
diff --git a/forum/skins/default/media/images/openid/openid.gif b/forum/skins/default/media/images/openid/openid.gif
index c718b0e6..c718b0e6 100644..100755
--- a/forum/skins/default/media/images/openid/openid.gif
+++ b/forum/skins/default/media/images/openid/openid.gif
Binary files differ
diff --git a/forum/skins/default/media/images/openid/technorati.ico b/forum/skins/default/media/images/openid/technorati.ico
index fa1083c1..fa1083c1 100644..100755
--- a/forum/skins/default/media/images/openid/technorati.ico
+++ b/forum/skins/default/media/images/openid/technorati.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/twitter.png b/forum/skins/default/media/images/openid/twitter.png
new file mode 100755
index 00000000..9a6552d1
--- /dev/null
+++ b/forum/skins/default/media/images/openid/twitter.png
Binary files differ
diff --git a/forum/skins/default/media/images/openid/verisign.ico b/forum/skins/default/media/images/openid/verisign.ico
index 3953af93..3953af93 100644..100755
--- a/forum/skins/default/media/images/openid/verisign.ico
+++ b/forum/skins/default/media/images/openid/verisign.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/vidoop.ico b/forum/skins/default/media/images/openid/vidoop.ico
index bbd9a0d5..bbd9a0d5 100644..100755
--- a/forum/skins/default/media/images/openid/vidoop.ico
+++ b/forum/skins/default/media/images/openid/vidoop.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/wordpress.ico b/forum/skins/default/media/images/openid/wordpress.ico
index 31b7d2c2..31b7d2c2 100644..100755
--- a/forum/skins/default/media/images/openid/wordpress.ico
+++ b/forum/skins/default/media/images/openid/wordpress.ico
Binary files differ
diff --git a/forum/skins/default/media/images/openid/yahoo.gif b/forum/skins/default/media/images/openid/yahoo.gif
index 42adbfa5..0f0eb8ef 100644..100755
--- a/forum/skins/default/media/images/openid/yahoo.gif
+++ b/forum/skins/default/media/images/openid/yahoo.gif
Binary files differ
diff --git a/forum/skins/default/media/images/quest-bg.gif b/forum/skins/default/media/images/quest-bg.gif
index b7540238..b7540238 100644..100755
--- a/forum/skins/default/media/images/quest-bg.gif
+++ b/forum/skins/default/media/images/quest-bg.gif
Binary files differ
diff --git a/forum/skins/default/media/images/vote-accepted-on.png b/forum/skins/default/media/images/vote-accepted-on.png
index 2026f3bc..2026f3bc 100644..100755
--- a/forum/skins/default/media/images/vote-accepted-on.png
+++ b/forum/skins/default/media/images/vote-accepted-on.png
Binary files differ
diff --git a/forum/skins/default/media/images/vote-accepted.png b/forum/skins/default/media/images/vote-accepted.png
index ecd18551..ecd18551 100644..100755
--- a/forum/skins/default/media/images/vote-accepted.png
+++ b/forum/skins/default/media/images/vote-accepted.png
Binary files differ
diff --git a/forum/skins/default/media/images/vote-arrow-down-on.png b/forum/skins/default/media/images/vote-arrow-down-on.png
index 048dbb44..048dbb44 100644..100755
--- a/forum/skins/default/media/images/vote-arrow-down-on.png
+++ b/forum/skins/default/media/images/vote-arrow-down-on.png
Binary files differ
diff --git a/forum/skins/default/media/images/vote-arrow-down.png b/forum/skins/default/media/images/vote-arrow-down.png
index e4fdec0a..e4fdec0a 100644..100755
--- a/forum/skins/default/media/images/vote-arrow-down.png
+++ b/forum/skins/default/media/images/vote-arrow-down.png
Binary files differ
diff --git a/forum/skins/default/media/images/vote-arrow-up-on.png b/forum/skins/default/media/images/vote-arrow-up-on.png
index 56ad0c25..56ad0c25 100644..100755
--- a/forum/skins/default/media/images/vote-arrow-up-on.png
+++ b/forum/skins/default/media/images/vote-arrow-up-on.png
Binary files differ
diff --git a/forum/skins/default/media/images/vote-arrow-up.png b/forum/skins/default/media/images/vote-arrow-up.png
index 6e9a51c7..6e9a51c7 100644..100755
--- a/forum/skins/default/media/images/vote-arrow-up.png
+++ b/forum/skins/default/media/images/vote-arrow-up.png
Binary files differ
diff --git a/forum/skins/default/media/images/vote-favorite-off.png b/forum/skins/default/media/images/vote-favorite-off.png
index c1bef074..c1bef074 100644..100755
--- a/forum/skins/default/media/images/vote-favorite-off.png
+++ b/forum/skins/default/media/images/vote-favorite-off.png
Binary files differ
diff --git a/forum/skins/default/media/images/vote-favorite-on.png b/forum/skins/default/media/images/vote-favorite-on.png
index 1f9c14ab..1f9c14ab 100644..100755
--- a/forum/skins/default/media/images/vote-favorite-on.png
+++ b/forum/skins/default/media/images/vote-favorite-on.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/aol.gif b/forum/skins/default/media/jquery-openid/images/aol.gif
index decc4f12..decc4f12 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/aol.gif
+++ b/forum/skins/default/media/jquery-openid/images/aol.gif
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/blogger-1.png b/forum/skins/default/media/jquery-openid/images/blogger-1.png
index 8b360ea5..8b360ea5 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/blogger-1.png
+++ b/forum/skins/default/media/jquery-openid/images/blogger-1.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/blogger.ico b/forum/skins/default/media/jquery-openid/images/blogger.ico
index 1b9730b0..1b9730b0 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/blogger.ico
+++ b/forum/skins/default/media/jquery-openid/images/blogger.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/claimid-0.png b/forum/skins/default/media/jquery-openid/images/claimid-0.png
index 4a0ea1b3..4a0ea1b3 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/claimid-0.png
+++ b/forum/skins/default/media/jquery-openid/images/claimid-0.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/claimid.ico b/forum/skins/default/media/jquery-openid/images/claimid.ico
index 2b80f491..2b80f491 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/claimid.ico
+++ b/forum/skins/default/media/jquery-openid/images/claimid.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/facebook.gif b/forum/skins/default/media/jquery-openid/images/facebook.gif
index b997b358..b997b358 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/facebook.gif
+++ b/forum/skins/default/media/jquery-openid/images/facebook.gif
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/flickr.ico b/forum/skins/default/media/jquery-openid/images/flickr.ico
index 11f6e07f..11f6e07f 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/flickr.ico
+++ b/forum/skins/default/media/jquery-openid/images/flickr.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/flickr.png b/forum/skins/default/media/jquery-openid/images/flickr.png
index 142405a6..142405a6 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/flickr.png
+++ b/forum/skins/default/media/jquery-openid/images/flickr.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/google.gif b/forum/skins/default/media/jquery-openid/images/google.gif
index 1b6cd07b..1b6cd07b 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/google.gif
+++ b/forum/skins/default/media/jquery-openid/images/google.gif
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/livejournal-1.png b/forum/skins/default/media/jquery-openid/images/livejournal-1.png
index e6436081..e6436081 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/livejournal-1.png
+++ b/forum/skins/default/media/jquery-openid/images/livejournal-1.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/livejournal.ico b/forum/skins/default/media/jquery-openid/images/livejournal.ico
index f3d21ec5..f3d21ec5 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/livejournal.ico
+++ b/forum/skins/default/media/jquery-openid/images/livejournal.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/myopenid-2.png b/forum/skins/default/media/jquery-openid/images/myopenid-2.png
index f64fb8e8..f64fb8e8 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/myopenid-2.png
+++ b/forum/skins/default/media/jquery-openid/images/myopenid-2.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/myopenid.ico b/forum/skins/default/media/jquery-openid/images/myopenid.ico
index ceb06e6a..ceb06e6a 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/myopenid.ico
+++ b/forum/skins/default/media/jquery-openid/images/myopenid.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/openid-inputicon.gif b/forum/skins/default/media/jquery-openid/images/openid-inputicon.gif
index cde836c8..cde836c8 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/openid-inputicon.gif
+++ b/forum/skins/default/media/jquery-openid/images/openid-inputicon.gif
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/openid.gif b/forum/skins/default/media/jquery-openid/images/openid.gif
index c718b0e6..c718b0e6 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/openid.gif
+++ b/forum/skins/default/media/jquery-openid/images/openid.gif
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/openidico.png b/forum/skins/default/media/jquery-openid/images/openidico.png
index ab622669..ab622669 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/openidico.png
+++ b/forum/skins/default/media/jquery-openid/images/openidico.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/openidico16.png b/forum/skins/default/media/jquery-openid/images/openidico16.png
index ad718ac5..ad718ac5 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/openidico16.png
+++ b/forum/skins/default/media/jquery-openid/images/openidico16.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/technorati-1.png b/forum/skins/default/media/jquery-openid/images/technorati-1.png
index f7195240..f7195240 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/technorati-1.png
+++ b/forum/skins/default/media/jquery-openid/images/technorati-1.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/technorati.ico b/forum/skins/default/media/jquery-openid/images/technorati.ico
index fa1083c1..fa1083c1 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/technorati.ico
+++ b/forum/skins/default/media/jquery-openid/images/technorati.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/verisign-2.png b/forum/skins/default/media/jquery-openid/images/verisign-2.png
index c1467008..c1467008 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/verisign-2.png
+++ b/forum/skins/default/media/jquery-openid/images/verisign-2.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/verisign.ico b/forum/skins/default/media/jquery-openid/images/verisign.ico
index 3953af93..3953af93 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/verisign.ico
+++ b/forum/skins/default/media/jquery-openid/images/verisign.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/vidoop.ico b/forum/skins/default/media/jquery-openid/images/vidoop.ico
index bbd9a0d5..bbd9a0d5 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/vidoop.ico
+++ b/forum/skins/default/media/jquery-openid/images/vidoop.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/vidoop.png b/forum/skins/default/media/jquery-openid/images/vidoop.png
index 032c9e98..032c9e98 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/vidoop.png
+++ b/forum/skins/default/media/jquery-openid/images/vidoop.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/wordpress.ico b/forum/skins/default/media/jquery-openid/images/wordpress.ico
index 31b7d2c2..31b7d2c2 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/wordpress.ico
+++ b/forum/skins/default/media/jquery-openid/images/wordpress.ico
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/wordpress.png b/forum/skins/default/media/jquery-openid/images/wordpress.png
index ee29f0cf..ee29f0cf 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/wordpress.png
+++ b/forum/skins/default/media/jquery-openid/images/wordpress.png
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/images/yahoo.gif b/forum/skins/default/media/jquery-openid/images/yahoo.gif
index 42adbfa5..42adbfa5 100644..100755
--- a/forum/skins/default/media/jquery-openid/images/yahoo.gif
+++ b/forum/skins/default/media/jquery-openid/images/yahoo.gif
Binary files differ
diff --git a/forum/skins/default/media/jquery-openid/jquery.openid.js b/forum/skins/default/media/jquery-openid/jquery.openid.js
index 8d1cd204..8d1cd204 100644..100755
--- a/forum/skins/default/media/jquery-openid/jquery.openid.js
+++ b/forum/skins/default/media/jquery-openid/jquery.openid.js
diff --git a/forum/skins/default/media/jquery-openid/openid.css b/forum/skins/default/media/jquery-openid/openid.css
index 1b7aaf82..1b7aaf82 100644..100755
--- a/forum/skins/default/media/jquery-openid/openid.css
+++ b/forum/skins/default/media/jquery-openid/openid.css
diff --git a/forum/skins/default/media/js/com.cnprog.admin.js b/forum/skins/default/media/js/com.cnprog.admin.js
index 39dff48c..39dff48c 100644..100755
--- a/forum/skins/default/media/js/com.cnprog.admin.js
+++ b/forum/skins/default/media/js/com.cnprog.admin.js
diff --git a/forum/skins/default/media/js/com.cnprog.editor.js b/forum/skins/default/media/js/com.cnprog.editor.js
index 18cc5166..18cc5166 100644..100755
--- a/forum/skins/default/media/js/com.cnprog.editor.js
+++ b/forum/skins/default/media/js/com.cnprog.editor.js
diff --git a/forum/skins/default/media/js/com.cnprog.i18n.js b/forum/skins/default/media/js/com.cnprog.i18n.js
index da9bf396..da9bf396 100644..100755
--- a/forum/skins/default/media/js/com.cnprog.i18n.js
+++ b/forum/skins/default/media/js/com.cnprog.i18n.js
diff --git a/forum/skins/default/media/js/com.cnprog.post.js b/forum/skins/default/media/js/com.cnprog.post.js
index 4325e665..4325e665 100644..100755
--- a/forum/skins/default/media/js/com.cnprog.post.js
+++ b/forum/skins/default/media/js/com.cnprog.post.js
diff --git a/forum/skins/default/media/js/com.cnprog.tag_selector.js b/forum/skins/default/media/js/com.cnprog.tag_selector.js
index e3279e65..e3279e65 100644..100755
--- a/forum/skins/default/media/js/com.cnprog.tag_selector.js
+++ b/forum/skins/default/media/js/com.cnprog.tag_selector.js
diff --git a/forum/skins/default/media/js/com.cnprog.utils.js b/forum/skins/default/media/js/com.cnprog.utils.js
index 3f7720c1..3f7720c1 100644..100755
--- a/forum/skins/default/media/js/com.cnprog.utils.js
+++ b/forum/skins/default/media/js/com.cnprog.utils.js
diff --git a/forum/skins/default/media/js/compress.bat b/forum/skins/default/media/js/compress.bat
index 5b2673cf..5b2673cf 100644..100755
--- a/forum/skins/default/media/js/compress.bat
+++ b/forum/skins/default/media/js/compress.bat
diff --git a/forum/skins/default/media/js/excanvas.pack.js b/forum/skins/default/media/js/excanvas.pack.js
index 71d6fbd9..71d6fbd9 100644..100755
--- a/forum/skins/default/media/js/excanvas.pack.js
+++ b/forum/skins/default/media/js/excanvas.pack.js
diff --git a/forum/skins/default/media/js/flot-build.bat b/forum/skins/default/media/js/flot-build.bat
index f9f32cb7..f9f32cb7 100644..100755
--- a/forum/skins/default/media/js/flot-build.bat
+++ b/forum/skins/default/media/js/flot-build.bat
diff --git a/forum/skins/default/media/js/jquery-1.2.6.js b/forum/skins/default/media/js/jquery-1.2.6.js
index 88e661ee..88e661ee 100644..100755
--- a/forum/skins/default/media/js/jquery-1.2.6.js
+++ b/forum/skins/default/media/js/jquery-1.2.6.js
diff --git a/forum/skins/default/media/js/jquery-1.2.6.min.js b/forum/skins/default/media/js/jquery-1.2.6.min.js
index 82b98e1d..82b98e1d 100644..100755
--- a/forum/skins/default/media/js/jquery-1.2.6.min.js
+++ b/forum/skins/default/media/js/jquery-1.2.6.min.js
diff --git a/forum/skins/default/media/js/jquery.ajaxfileupload.js b/forum/skins/default/media/js/jquery.ajaxfileupload.js
index 75292776..75292776 100644..100755
--- a/forum/skins/default/media/js/jquery.ajaxfileupload.js
+++ b/forum/skins/default/media/js/jquery.ajaxfileupload.js
diff --git a/forum/skins/default/media/js/jquery.flot.js b/forum/skins/default/media/js/jquery.flot.js
index 83b61929..83b61929 100644..100755
--- a/forum/skins/default/media/js/jquery.flot.js
+++ b/forum/skins/default/media/js/jquery.flot.js
diff --git a/forum/skins/default/media/js/jquery.flot.pack.js b/forum/skins/default/media/js/jquery.flot.pack.js
index a5714f12..a5714f12 100644..100755
--- a/forum/skins/default/media/js/jquery.flot.pack.js
+++ b/forum/skins/default/media/js/jquery.flot.pack.js
diff --git a/forum/skins/default/media/js/jquery.form.js b/forum/skins/default/media/js/jquery.form.js
index 443114fd..443114fd 100644..100755
--- a/forum/skins/default/media/js/jquery.form.js
+++ b/forum/skins/default/media/js/jquery.form.js
diff --git a/forum/skins/default/media/js/jquery.i18n.js b/forum/skins/default/media/js/jquery.i18n.js
index 0a155a31..0a155a31 100644..100755
--- a/forum/skins/default/media/js/jquery.i18n.js
+++ b/forum/skins/default/media/js/jquery.i18n.js
diff --git a/forum/skins/default/media/js/jquery.openid.js b/forum/skins/default/media/js/jquery.openid.js
index af7d8cb9..af7d8cb9 100644..100755
--- a/forum/skins/default/media/js/jquery.openid.js
+++ b/forum/skins/default/media/js/jquery.openid.js
diff --git a/forum/skins/default/media/js/jquery.validate.pack.js b/forum/skins/default/media/js/jquery.validate.pack.js
index 49134500..49134500 100644..100755
--- a/forum/skins/default/media/js/jquery.validate.pack.js
+++ b/forum/skins/default/media/js/jquery.validate.pack.js
diff --git a/forum/skins/default/media/js/se_hilite.js b/forum/skins/default/media/js/se_hilite.js
index 42e99c8e..42e99c8e 100644..100755
--- a/forum/skins/default/media/js/se_hilite.js
+++ b/forum/skins/default/media/js/se_hilite.js
diff --git a/forum/skins/default/media/js/se_hilite_src.js b/forum/skins/default/media/js/se_hilite_src.js
index b604f156..b604f156 100644..100755
--- a/forum/skins/default/media/js/se_hilite_src.js
+++ b/forum/skins/default/media/js/se_hilite_src.js
diff --git a/forum/skins/default/media/js/wmd/images/wmd-buttons.png b/forum/skins/default/media/js/wmd/images/wmd-buttons.png
index 50b37090..50b37090 100644..100755
--- a/forum/skins/default/media/js/wmd/images/wmd-buttons.png
+++ b/forum/skins/default/media/js/wmd/images/wmd-buttons.png
Binary files differ
diff --git a/forum/skins/default/media/js/wmd/showdown-min.js b/forum/skins/default/media/js/wmd/showdown-min.js
index 073613b1..073613b1 100644..100755
--- a/forum/skins/default/media/js/wmd/showdown-min.js
+++ b/forum/skins/default/media/js/wmd/showdown-min.js
diff --git a/forum/skins/default/media/js/wmd/showdown.js b/forum/skins/default/media/js/wmd/showdown.js
index 3f4b9947..3f4b9947 100644..100755
--- a/forum/skins/default/media/js/wmd/showdown.js
+++ b/forum/skins/default/media/js/wmd/showdown.js
diff --git a/forum/skins/default/media/js/wmd/wmd-min.js b/forum/skins/default/media/js/wmd/wmd-min.js
index aa643f1a..aa643f1a 100644..100755
--- a/forum/skins/default/media/js/wmd/wmd-min.js
+++ b/forum/skins/default/media/js/wmd/wmd-min.js
diff --git a/forum/skins/default/media/js/wmd/wmd-test.html b/forum/skins/default/media/js/wmd/wmd-test.html
index d748501a..d748501a 100644..100755
--- a/forum/skins/default/media/js/wmd/wmd-test.html
+++ b/forum/skins/default/media/js/wmd/wmd-test.html
diff --git a/forum/skins/default/media/js/wmd/wmd.css b/forum/skins/default/media/js/wmd/wmd.css
index 80c226c8..80c226c8 100644..100755
--- a/forum/skins/default/media/js/wmd/wmd.css
+++ b/forum/skins/default/media/js/wmd/wmd.css
diff --git a/forum/skins/default/media/js/wmd/wmd.js b/forum/skins/default/media/js/wmd/wmd.js
index 70271d4d..70271d4d 100644..100755
--- a/forum/skins/default/media/js/wmd/wmd.js
+++ b/forum/skins/default/media/js/wmd/wmd.js
diff --git a/forum/skins/default/media/js/yuicompressor-2.4.2.jar b/forum/skins/default/media/js/yuicompressor-2.4.2.jar
index c29470bd..c29470bd 100644..100755
--- a/forum/skins/default/media/js/yuicompressor-2.4.2.jar
+++ b/forum/skins/default/media/js/yuicompressor-2.4.2.jar
Binary files differ
diff --git a/forum/skins/default/media/style/auth.css b/forum/skins/default/media/style/auth.css
new file mode 100755
index 00000000..64ec6f3e
--- /dev/null
+++ b/forum/skins/default/media/style/auth.css
@@ -0,0 +1,48 @@
+#bigicon_providers, #smallicon_providers {
+ display: block;
+ padding: 0px;
+ width:600px;
+ margin:0px 0px 5px 0px;
+}
+
+.provider_logo {
+ display: inline-block;
+ padding: 4px;
+ border: 1px solid #DDD;
+ text-align: center;
+ vertical-align: middle;
+}
+
+.provider_logo.big {
+ height: 40px;
+ width: 90px;
+}
+
+.provider_logo.small {
+ height: 32px;
+ width: 32px;
+}
+
+.provider_logo.selected {
+ outline: 2px solid #FFF8C6;
+}
+
+.provider_logo .provider_url {
+ display: none;
+}
+
+.signin_form input[type="text"], .signin_form input[type="password"], .signin_form input[type="submit"] {
+ height: 28px;
+ line-height: 22px;
+ font-size: 140%;
+ border: 1px solid #999;
+}
+
+.signin_form .icon_input {
+ padding-left: 20px;
+}
+
+.or_label {
+ margin-top: 20px;
+ margin-bottom: 10px;
+} \ No newline at end of file
diff --git a/forum/skins/default/media/style/default.css b/forum/skins/default/media/style/default.css
index 27da1dab..27da1dab 100644..100755
--- a/forum/skins/default/media/style/default.css
+++ b/forum/skins/default/media/style/default.css
diff --git a/forum/skins/default/media/style/jquery.autocomplete.css b/forum/skins/default/media/style/jquery.autocomplete.css
index 3bf2c2d9..3bf2c2d9 100644..100755
--- a/forum/skins/default/media/style/jquery.autocomplete.css
+++ b/forum/skins/default/media/style/jquery.autocomplete.css
diff --git a/forum/skins/default/media/style/openid.css b/forum/skins/default/media/style/openid.css
index 0d201df2..0d201df2 100644..100755
--- a/forum/skins/default/media/style/openid.css
+++ b/forum/skins/default/media/style/openid.css
diff --git a/forum/skins/default/media/style/prettify.css b/forum/skins/default/media/style/prettify.css
index 10a37577..10a37577 100644..100755
--- a/forum/skins/default/media/style/prettify.css
+++ b/forum/skins/default/media/style/prettify.css
diff --git a/forum/skins/default/media/style/style.css b/forum/skins/default/media/style/style.css
index 02148ab0..02148ab0 100644..100755
--- a/forum/skins/default/media/style/style.css
+++ b/forum/skins/default/media/style/style.css
diff --git a/forum/skins/default/templates/404.html b/forum/skins/default/templates/404.html
index 227de3ae..227de3ae 100644..100755
--- a/forum/skins/default/templates/404.html
+++ b/forum/skins/default/templates/404.html
diff --git a/forum/skins/default/templates/500.html b/forum/skins/default/templates/500.html
index 51e73178..51e73178 100644..100755
--- a/forum/skins/default/templates/500.html
+++ b/forum/skins/default/templates/500.html
diff --git a/forum/skins/default/templates/about.html b/forum/skins/default/templates/about.html
index 66dcc3fd..e463ac44 100644..100755
--- a/forum/skins/default/templates/about.html
+++ b/forum/skins/default/templates/about.html
@@ -26,7 +26,7 @@
These points (very) roughly reflect the level of trust of the community.
</p>
<p>No points are necessary to ask or answer the questions - so please -
- <strong><a href="{% url user_signin %}">join us!</a></strong>
+ <strong><a href="{% url auth_signin %}">join us!</a></strong>
</p>
<p>
If you would like to find out more about this site - please see <strong><a href="{% url faq %}">frequently asked questions</a></strong>.
diff --git a/forum/skins/default/templates/account_settings.html b/forum/skins/default/templates/account_settings.html
new file mode 100755
index 00000000..91267d26
--- /dev/null
+++ b/forum/skins/default/templates/account_settings.html
@@ -0,0 +1,45 @@
+{% extends "base_content.html" %}
+<!-- settings.html -->
+{% load i18n %}
+{% block title %}{% spaceless %}{% trans "Account functions" %}{% endspaceless %}{% endblock %}
+{% block head %}
+<style type="text/css" media="screen">
+ h4 {font-size:12pt;}
+ dt, dd { padding:0 0 0.35em 0; }
+ dt { float: left; width: 21ex; }
+ dd { margin-left: 23ex; }
+
+ #settings-options, #settings-intro { padding: 4em 1.5em;}
+ #settings-options { min-height: 300px; border-left: 1px solid #333;}
+
+ #settings-options h5 { font-weight: bold;}
+</style>
+{% endblock %}
+
+{% block content %}
+<div id="main-bar">
+ <h3><strong>{{ request.user.username }} {% trans "Profile" %}</strong></h3>
+</div>
+<div id="settings-options">
+ {% if msg %}
+ <p class="error">{{ msg }}</p>
+ {% endif %}
+
+ <dl class="list-item">
+ <dt>» <a href="{% url user_changepw %}">{% trans "Change password" %}</a></dt>
+ <dd>{% trans "Give your account a new password." %}</dd>
+ {% comment %}
+ <dt>» <a href="{% url user_changeemail %}">{% trans "Change email " %}</a></dt>
+ <dd>{% trans "Add or update the email address associated with your account." %}</dd>
+
+ <dt>» <a href="{% url user_changeopenid %}">{% trans "Change OpenID" %}</a></dt>
+ <dd>{% trans "Change openid associated to your account" %}</dd>
+
+
+ <dt>» <a href="{% url user_delete %}">{% trans "Delete account" %}</a></dt>
+ <dd>{% trans "Erase your username and all your data from website" %}</dd>
+ {% endcomment %}
+ </dl>
+</div>
+{% endblock %}
+<!-- end settings.html -->
diff --git a/forum/skins/default/templates/answer_edit.html b/forum/skins/default/templates/answer_edit.html
index 2d736f30..2d736f30 100644..100755
--- a/forum/skins/default/templates/answer_edit.html
+++ b/forum/skins/default/templates/answer_edit.html
diff --git a/forum/skins/default/templates/answer_edit_tips.html b/forum/skins/default/templates/answer_edit_tips.html
index c390da06..c390da06 100644..100755
--- a/forum/skins/default/templates/answer_edit_tips.html
+++ b/forum/skins/default/templates/answer_edit_tips.html
diff --git a/forum/skins/default/templates/ask.html b/forum/skins/default/templates/ask.html
index 083b01d9..083b01d9 100644..100755
--- a/forum/skins/default/templates/ask.html
+++ b/forum/skins/default/templates/ask.html
diff --git a/forum/skins/default/templates/auth/complete.html b/forum/skins/default/templates/auth/complete.html
new file mode 100755
index 00000000..cb2dc5aa
--- /dev/null
+++ b/forum/skins/default/templates/auth/complete.html
@@ -0,0 +1,95 @@
+{% extends "base_content.html" %}
+<!-- complete.html -->
+{% load i18n %}
+{% block head %}{% endblock %}
+{% block title %}{% spaceless %}{% trans "Connect your OpenID with this site" %}{% endspaceless %}{% endblock %}
+{% block content %}
+ <div id="main-bar" class="headNormal">
+ {% trans "Connect your OpenID with your account on this site" %}
+ </div>
+ <div id="completetxt" >
+ <div class="message">
+ <b>{% trans "You are here for the first time with " %}{{ provider }}</b>
+ {% trans "Please create your screen name and save your email address. Saved email address will let you subscribe for the updates on the most interesting questions and will be used to create and retrieve your unique avatar image. " %}
+ </div>
+ <p style="display:none">{% trans "This account already exists, please use another." %}</p>
+ </div>
+
+ {% if form1.errors %}
+ <ul class="errorlist">
+ {% if form1.non_field_errors %}
+ {% for error in form1.non_field_errors %}
+ <li>{{error}}</li>
+ {% endfor %}
+ {% endif %}
+ </ul>
+ {% endif %}
+ {% comment %}
+ {% if form2.errors %}<!--form2 is dysfunctional so commented out -->
+ <div class="errors">
+ <span class="big">{% trans "Sorry, looks like we have some errors:" %}</span><br/>
+ <ul class="error-list">
+ {% if form2.username.errors %}
+ <li><span class="error">{{ form2.username.errors|join:", " }}</span></li>
+ {% endif %}
+ {% if form2.password.errors %}
+ <li><span class="error">{{ form2.password.errors|join:", " }}</span></li>
+ {% endif %}
+ </ul>
+ </div>
+ {% endif %}
+ {% endcomment %}
+
+ <div class="login">
+ <form name="fregister" action="" method="POST">
+ {{ form1.next }}
+ <div class="form-row-vertical">
+ <label for="id_username">{% trans "Screen name label" %}</label>
+ {% if form1.username.errors %}
+ <p class="error">{{ form1.username.errors|join:", " }}</p>
+ {% endif %}
+ {{ form1.username }}
+ </div>
+ <div class="form-row-vertical margin-bottom">
+ <label for="id_email">{% trans "Email address label" %}</label>
+ {% if form1.email.errors %}
+ <p class="error">{{ form1.email.errors|join:", " }}</p>
+ {% endif %}
+ {{ form1.email }}
+ </div>
+ <p>{% trans "receive updates motivational blurb" %}</p>
+ <div class='simple-subscribe-options'>
+ {{email_feeds_form.subscribe}}
+ {% if email_feeds_form.errors %}
+ <p class="error">{% trans "please select one of the options above" %}</p>
+ {% endif %}
+ </div>
+ <p class='space-above'>{% trans "Tag filter tool will be your right panel, once you log in." %}</p>
+ <div class="submit-row"><input type="submit" class="submit" name="bnewaccount" value="{% trans "create account" %}"/></div>
+ </form>
+ </div>
+ {% comment %}<!-- this form associates openID with an existing password-protected account, not yet functional -->
+ {% if form2 %}
+ <div class="login" style="display:none">
+ <form name="fverify" action="{% url user_register %}" method="POST">
+ {{ form2.next }}
+ <fieldset style="padding:10px">
+ <legend class="big">{% trans "Existing account" %}</legend>
+ <div class="form-row"><label for="id_username">{% trans "user name" %}</label><br/>{{ form2.username }}</div>
+ <div class="form-row"><label for="id_passwordl">{% trans "password" %}</label><br/>{{ form2.password }}</div>
+ <p><span class='big strong'>(Optional) receive updates by email</span> - only sent when there are any.</p>
+ <div class='simple-subscribe-options'>
+ {{email_feeds_form.subscribe}}
+ </div>
+ <!--todo double check translation from chinese 确认 = "Register" -->
+ <div class="submit-row">
+ <input type="submit" class="submit" name="bverify" value="{% trans "Register" %}"/>
+ <a href="{% url user_sendpw %}">{% trans "Forgot your password?" %}</a>
+ </div>
+ </fieldset>
+ </form>
+ </div>
+ {% endif %}
+ {% endcomment %}
+{% endblock %}
+<!-- end complete.html -->
diff --git a/forum/skins/default/templates/auth/signin.html b/forum/skins/default/templates/auth/signin.html
new file mode 100755
index 00000000..d4ee9fc1
--- /dev/null
+++ b/forum/skins/default/templates/auth/signin.html
@@ -0,0 +1,161 @@
+{% extends "base.html" %}
+
+{% load i18n %}
+{% load extra_tags %}
+
+{% block forejs %}
+ <link rel="stylesheet" type="text/css" media="screen" href="{% media "/media/style/auth.css" %}"/>
+ {% for provider in all_providers %}
+ {% for location in provider.extra_css %}
+ <link rel="stylesheet" type="text/css" media="screen" href="{{ location }}"/>
+ {% endfor %}
+ {% endfor %}
+{% endblock %}
+
+{% block content %}
+ {% for provider in all_providers %}
+ {% if provider.pre_code %}
+ {{ provider.pre_code|safe }}
+ {% endif %}
+ {% endfor %}
+ <div class="headNormal">
+ {% trans "User login" %}
+ </div>
+ {% if msg %}
+ <p class="warning">{{ msg }}</p>
+ {% endif %}
+
+ <div style="width:600px;float:left;margin-bottom:5px;">
+ {% trans "Click to sign in through any of these services." %}
+ </div>
+ {% if request.user.is_anonymous %}
+ <div style="width:600px;float:left;margin-bottom:5px;">
+ <input type="checkbox" checked="checked" id="validate_email" />
+ {% trans "Take the opurtunity to validate my email next to the external provider I choose." %}
+ </div>
+ {% endif %}
+ <div id="bigicon_providers">
+ {% for provider in bigicon_providers %}
+ <div class="provider_logo big" name="{{ provider.id }}">
+ {% ifequal provider.type "DIRECT" %}
+ <a class="provider_direct" href="{% url auth_provider_signin provider=provider.id %}">
+ <img src="{% media provider.icon %}" />
+ </a>
+ {% endifequal %}
+ {% ifequal provider.type "CUSTOM" %}
+ {% include provider.code_template %}
+ {% endifequal %}
+ {% ifequal provider.type "SIMPLE_FORM" %}
+ <img alt="{{ provider.simple_form_context.your_what }}" class="simple_form_provider" src="{% media provider.icon %}" />
+ {% endifequal %}
+ </div>
+ {% endfor %}
+ </div>
+ <div id="smallicon_providers">
+ {% for provider in smallicon_providers %}
+ <div class="provider_logo small" name="{{ provider.id }}">
+ {% ifequal provider.type "DIRECT" %}
+ <a class="provider_direct" href="{% url auth_provider_signin provider=provider.id %}">
+ <img src="{% media provider.icon %}" />
+ </a>
+ {% endifequal %}
+ {% ifequal provider.type "CUSTOM" %}
+ {% include provider.code_template %}
+ {% endifequal %}
+ {% ifequal provider.type "SIMPLE_FORM" %}
+ <img alt="{{ provider.simple_form_context.your_what }}" class="simple_form_provider" src="{% media provider.icon %}" />
+ {% endifequal %}
+ </div>
+ {% endfor %}
+ </div>
+ <form name="signin_form" id="signin_form" class="signin_form" method="POST" action="">
+ <div id="signin_form_slot"></div>
+ <input type="hidden" class="validate_email" name="validate_email" value="yes" />
+ </form>
+ {% for provider in stackitem_providers %}
+ <h3 class="or_label">Or...</h3>
+ <form class="signin_form" method="POST" action="{% url auth_provider_signin provider=provider.id %}">
+ {% include provider.stack_item_template %}
+ <input type="hidden" class="validate_email" name="validate_email" value="yes" />
+ </form>
+ {% endfor %}
+ <script type="text/html" id="simple_form_template">
+ <fieldset id="slot_form">
+ <p id="provider_name_slot">{% trans 'Enter your ' %}%%YOUR_WHAT%%</p>
+ <div><p><span></span>
+ <input id="input_field" type="text" name="input_field" /><span></span>
+ <input id="ssignin" name="ssignin" type="submit" value="Login" />
+ </p></div>
+ <input type="hidden" class="validate_email" name="validate_email" value="yes" />
+ </fieldset>
+ </script>
+ <script type="text/javascript">
+ $(function() {
+ var signin_url = "{% url auth_provider_signin provider='PROVIDER' %}";
+
+ function set_validate_email() {
+ var validate = $('#validate_email').attr('checked') ? 'yes' : 'no';
+ $('.validate_email').attr('value', validate);
+
+ $('.provider_direct').each(function() {
+ var current_url = $(this).attr('href');
+ if (!/\?validate_email\=(yes|no)$/.test(current_url)) {
+ current_url += ('?validate_email=' + validate);
+ } else {
+ current_url = current_url.replace(/(yes|no)$/, validate);
+ }
+
+ $(this).attr('href', current_url);
+ })
+ }
+
+ $('#validate_email').change(set_validate_email);
+
+ function set_form_action(el) {
+ var provider = el.parents('.provider_logo').attr('name');
+ $('#signin_form').attr('action', signin_url.replace('PROVIDER', provider));
+ }
+
+ $('.provider_logo').click(function() {
+ $('.provider_logo').removeClass('selected');
+ $(this).addClass('selected');
+ });
+
+ $('.simple_form_provider').click(function() {
+ $('#signin_form_slot').html('');
+ var new_html = $('#simple_form_template').html()
+ .replace('%%YOUR_WHAT%%', $(this).attr('alt'));
+ $('#signin_form_slot').html(new_html);
+ set_form_action($(this));
+ set_validate_email();
+ })
+
+ set_validate_email();
+ });
+ </script>
+{% endblock %}
+
+{% block sidebar %}
+<div class="boxC">
+ <h3 class="subtitle">{% trans "Why use OpenID?" %}</h3>
+ <ul class="list-item">
+ <li>
+ {% trans "with openid it is easier" %}
+ </li>
+ <li>
+ {% trans "reuse openid" %}
+ </li>
+ <li>
+ {% trans "openid is widely adopted" %}
+ </li>
+ <li>
+ {% trans "openid is supported open standard" %}
+ </li>
+
+ </ul>
+ <p class="info-box-follow-up-links">
+ <a href="http://openid.net/what/" target="_blank">{% trans "Find out more" %} </a><br/>
+ <a href="http://openid.net/get/" target="_blank">{% trans "Get OpenID" %} </a>
+ </p>
+</div>
+{% endblock%} \ No newline at end of file
diff --git a/forum/skins/default/templates/auth/signup.html b/forum/skins/default/templates/auth/signup.html
new file mode 100755
index 00000000..bd5750b4
--- /dev/null
+++ b/forum/skins/default/templates/auth/signup.html
@@ -0,0 +1,32 @@
+{% extends "base_content.html" %}
+<!--signup.html-->
+{% load i18n %}
+{% block title %}{% spaceless %}{% trans "Signup" %}{% endspaceless %}{% endblock %}
+
+{% block content %}
+<div class="headNormal">
+ {% trans "Create login name and password" %}
+</div>
+<p class="message">{% trans "Traditional signup info" %}</p>
+<form action="" method="post" accept-charset="utf-8">
+ <ul class="form-horizontal-rows">
+ <li><label for="usename_id">{{form.username.label}}</label>{{form.username}}{{form.username.errors}}</li>
+ <li><label for="email_id">{{form.email.label}}</label>{{form.email}}{{form.email.errors}}</li>
+ <li><label for="password1_id">{{form.password1.label}}</label>{{form.password1}}{{form.password1.errors}}</li>
+ <li><label for="password2_id">{{form.password2.label}}</label>{{form.password2}}{{form.password2.errors}}</li>
+ </ul>
+ <p class="margin-top">{% trans "receive updates motivational blurb" %}</p>
+ <div class='simple-subscribe-options'>
+ {{ email_feeds_form.subscribe }}
+ {% if email_feeds_form.errors %}
+ <p class="error">{% trans "please select one of the options above" %}</p>
+ {% endif %}
+ </div>
+ {% comment %}<p class="signup_p">{% trans "Please read and type in the two words below to help us prevent automated account creation." %}</p>
+ {{form.recaptcha}}{% endcomment %}
+ <div class="submit-row"><input type="submit" class="submit" value="{% trans "Create Account" %}" />
+ <strong>{% trans "or" %}
+ <a href="{% url auth_signin %}">{% trans "return to login page" %}</a></strong></div>
+</form>
+{% endblock %}
+<!--end signup.html-->
diff --git a/forum/skins/default/templates/badge.html b/forum/skins/default/templates/badge.html
index af6aa2a2..af6aa2a2 100644..100755
--- a/forum/skins/default/templates/badge.html
+++ b/forum/skins/default/templates/badge.html
diff --git a/forum/skins/default/templates/badges.html b/forum/skins/default/templates/badges.html
index 8de93df5..8de93df5 100644..100755
--- a/forum/skins/default/templates/badges.html
+++ b/forum/skins/default/templates/badges.html
diff --git a/forum/skins/default/templates/base_content.html b/forum/skins/default/templates/base_content.html
index d1cf673b..d1cf673b 100644..100755
--- a/forum/skins/default/templates/base_content.html
+++ b/forum/skins/default/templates/base_content.html
diff --git a/forum/skins/default/templates/book.html b/forum/skins/default/templates/book.html
index 8574fa73..8574fa73 100644..100755
--- a/forum/skins/default/templates/book.html
+++ b/forum/skins/default/templates/book.html
diff --git a/forum/skins/default/templates/changepw.html b/forum/skins/default/templates/changepw.html
new file mode 100755
index 00000000..7b4cf801
--- /dev/null
+++ b/forum/skins/default/templates/changepw.html
@@ -0,0 +1,18 @@
+{% extends "base.html" %}
+<!-- changepw.html -->
+{% load i18n %}
+{% block head %}{% endblock %}
+{% block title %}{% spaceless %}{% trans "Change password" %}{% endspaceless %}{% endblock %}
+{% block content %}
+<div class="headNormal">{% trans "Account: change password" %}</div>
+<p class="message">{% blocktrans %}This is where you can change your password. Make sure you remember it!{% endblocktrans %}</p>
+<div class="aligned">
+ <form action="" method="post" accept-charset="utf-8">
+ <ul id="changepw-form" class="form-horizontal-rows">
+ {{form.as_ul}}
+ </ul>
+ <div class="submit-row"><input type="submit" class="submit" value="{% trans "Change password" %}" /></div>
+ </form>
+ </div>
+{% endblock %}
+<!-- end changepw.html -->
diff --git a/forum/skins/default/templates/close.html b/forum/skins/default/templates/close.html
index d9e73507..d9e73507 100644..100755
--- a/forum/skins/default/templates/close.html
+++ b/forum/skins/default/templates/close.html
diff --git a/forum/skins/default/templates/edit_user_email_feeds_form.html b/forum/skins/default/templates/edit_user_email_feeds_form.html
index 65902e7e..65902e7e 100644..100755
--- a/forum/skins/default/templates/edit_user_email_feeds_form.html
+++ b/forum/skins/default/templates/edit_user_email_feeds_form.html
diff --git a/forum/skins/default/templates/faq.html b/forum/skins/default/templates/faq.html
index 236f4f76..284e1229 100644..100755
--- a/forum/skins/default/templates/faq.html
+++ b/forum/skins/default/templates/faq.html
@@ -97,6 +97,7 @@
</table>
</div>
+ {% comment %}
{% ifequal settings.EMAIL_VALIDATION 'on' %}
<div>
<a id='validate'></a><h3 class="subtitle">{% trans "how to validate email title" %}</h3>
@@ -104,6 +105,7 @@
{% blocktrans %}how to validate email info with {{send_email_key_url}} {{gravatar_faq_url}}{% endblocktrans %}
</div>
{% endifequal %}
+ {% endcomment %}
<div>
<a id='gravatar'></a><h3 class="subtitle">{% trans "what is gravatar" %}</h3>
<p>{% trans "gravatar faq info" %}</p>
@@ -111,7 +113,7 @@
<div>
<h3 class="subtitle">{% trans "To register, do I need to create new password?" %}</h3>
<p>{% trans "No, you don't have to. You can login through any service that supports OpenID, e.g. Google, Yahoo, AOL, etc." %}
- <strong><a href="{% url user_signin %}">{% trans "Login now!" %}</a> »</strong>
+ <strong><a href="{% url auth_signin %}">{% trans "Login now!" %}</a> »</strong>
</p>
</div>
diff --git a/forum/skins/default/templates/feedback.html b/forum/skins/default/templates/feedback.html
index 38bb48ff..38bb48ff 100644..100755
--- a/forum/skins/default/templates/feedback.html
+++ b/forum/skins/default/templates/feedback.html
diff --git a/forum/skins/default/templates/feedback_email.txt b/forum/skins/default/templates/feedback_email.txt
index df768180..df768180 100644..100755
--- a/forum/skins/default/templates/feedback_email.txt
+++ b/forum/skins/default/templates/feedback_email.txt
diff --git a/forum/skins/default/templates/feeds/rss_description.html b/forum/skins/default/templates/feeds/rss_description.html
index fa781907..fa781907 100644..100755
--- a/forum/skins/default/templates/feeds/rss_description.html
+++ b/forum/skins/default/templates/feeds/rss_description.html
diff --git a/forum/skins/default/templates/feeds/rss_title.html b/forum/skins/default/templates/feeds/rss_title.html
index 7899fce3..7899fce3 100644..100755
--- a/forum/skins/default/templates/feeds/rss_title.html
+++ b/forum/skins/default/templates/feeds/rss_title.html
diff --git a/forum/skins/default/templates/footer.html b/forum/skins/default/templates/footer.html
index 89d4801f..89d4801f 100644..100755
--- a/forum/skins/default/templates/footer.html
+++ b/forum/skins/default/templates/footer.html
diff --git a/forum/skins/default/templates/header.html b/forum/skins/default/templates/header.html
index 3afc46c5..cb12bb97 100644..100755
--- a/forum/skins/default/templates/header.html
+++ b/forum/skins/default/templates/header.html
@@ -8,7 +8,7 @@
<a href="{% url users %}{{ request.user.id }}/{{ request.user.username }}/">{{ request.user.username }}</a> {% get_score_badge request.user %}
<a href="{% url logout %}">{% trans "logout" %}</a>
{% else %}
- <a href="{% url user_signin %}">{% trans "login" %}</a>
+ <a href="{% url auth_signin %}">{% trans "login" %}</a>
{% endif %}
<a href="{% url about %}">{% trans "about" %}</a>
<a href="{% url faq %}">{% trans "faq" %}</a>
diff --git a/forum/skins/default/templates/logout.html b/forum/skins/default/templates/logout.html
index 650ba044..650ba044 100644..100755
--- a/forum/skins/default/templates/logout.html
+++ b/forum/skins/default/templates/logout.html
diff --git a/forum/skins/default/templates/notarobot.html b/forum/skins/default/templates/notarobot.html
index 698c5696..698c5696 100644..100755
--- a/forum/skins/default/templates/notarobot.html
+++ b/forum/skins/default/templates/notarobot.html
diff --git a/forum/skins/default/templates/pagesize.html b/forum/skins/default/templates/pagesize.html
index 5037f1f6..5037f1f6 100644..100755
--- a/forum/skins/default/templates/pagesize.html
+++ b/forum/skins/default/templates/pagesize.html
diff --git a/forum/skins/default/templates/paginator.html b/forum/skins/default/templates/paginator.html
index 2fba5425..2fba5425 100644..100755
--- a/forum/skins/default/templates/paginator.html
+++ b/forum/skins/default/templates/paginator.html
diff --git a/forum/skins/default/templates/post_contributor_info.html b/forum/skins/default/templates/post_contributor_info.html
index 9997be5f..9997be5f 100644..100755
--- a/forum/skins/default/templates/post_contributor_info.html
+++ b/forum/skins/default/templates/post_contributor_info.html
diff --git a/forum/skins/default/templates/privacy.html b/forum/skins/default/templates/privacy.html
index e66086dd..e66086dd 100644..100755
--- a/forum/skins/default/templates/privacy.html
+++ b/forum/skins/default/templates/privacy.html
diff --git a/forum/skins/default/templates/question.html b/forum/skins/default/templates/question.html
index fe9f5cde..23c24113 100644..100755
--- a/forum/skins/default/templates/question.html
+++ b/forum/skins/default/templates/question.html
@@ -32,7 +32,7 @@
{% if not question.closed and request.user.is_authenticated %}initEditor();{% endif %}
lanai.highlightSyntax();
- $('#btLogin').bind('click', function(){window.location.href='{% url user_signin %}'; } )
+ $('#btLogin').bind('click', function(){window.location.href='{% url auth_signin %}'; } )
});
function initEditor(){
diff --git a/forum/skins/default/templates/question_edit.html b/forum/skins/default/templates/question_edit.html
index fe711849..fe711849 100644..100755
--- a/forum/skins/default/templates/question_edit.html
+++ b/forum/skins/default/templates/question_edit.html
diff --git a/forum/skins/default/templates/question_edit_tips.html b/forum/skins/default/templates/question_edit_tips.html
index 4cabea79..4cabea79 100644..100755
--- a/forum/skins/default/templates/question_edit_tips.html
+++ b/forum/skins/default/templates/question_edit_tips.html
diff --git a/forum/skins/default/templates/question_retag.html b/forum/skins/default/templates/question_retag.html
index 03f3da04..03f3da04 100644..100755
--- a/forum/skins/default/templates/question_retag.html
+++ b/forum/skins/default/templates/question_retag.html
diff --git a/forum/skins/default/templates/question_summary_list_roll.html b/forum/skins/default/templates/question_summary_list_roll.html
index 57685d6d..57685d6d 100644..100755
--- a/forum/skins/default/templates/question_summary_list_roll.html
+++ b/forum/skins/default/templates/question_summary_list_roll.html
diff --git a/forum/skins/default/templates/questions.html b/forum/skins/default/templates/questions.html
index 4c3b96d2..4c3b96d2 100644..100755
--- a/forum/skins/default/templates/questions.html
+++ b/forum/skins/default/templates/questions.html
diff --git a/forum/skins/default/templates/reopen.html b/forum/skins/default/templates/reopen.html
index 37fb69c1..37fb69c1 100644..100755
--- a/forum/skins/default/templates/reopen.html
+++ b/forum/skins/default/templates/reopen.html
diff --git a/forum/skins/default/templates/revisions_answer.html b/forum/skins/default/templates/revisions_answer.html
index b2e33dfe..b2e33dfe 100644..100755
--- a/forum/skins/default/templates/revisions_answer.html
+++ b/forum/skins/default/templates/revisions_answer.html
diff --git a/forum/skins/default/templates/revisions_question.html b/forum/skins/default/templates/revisions_question.html
index 86d52a36..86d52a36 100644..100755
--- a/forum/skins/default/templates/revisions_question.html
+++ b/forum/skins/default/templates/revisions_question.html
diff --git a/forum/skins/default/templates/tag_selector.html b/forum/skins/default/templates/tag_selector.html
index 7686d717..7686d717 100644..100755
--- a/forum/skins/default/templates/tag_selector.html
+++ b/forum/skins/default/templates/tag_selector.html
diff --git a/forum/skins/default/templates/tags.html b/forum/skins/default/templates/tags.html
index 50f90fb1..50f90fb1 100644..100755
--- a/forum/skins/default/templates/tags.html
+++ b/forum/skins/default/templates/tags.html
diff --git a/forum/skins/default/templates/user.html b/forum/skins/default/templates/user.html
index 5931f31c..5931f31c 100644..100755
--- a/forum/skins/default/templates/user.html
+++ b/forum/skins/default/templates/user.html
diff --git a/forum/skins/default/templates/user_edit.html b/forum/skins/default/templates/user_edit.html
index 040ebff4..040ebff4 100644..100755
--- a/forum/skins/default/templates/user_edit.html
+++ b/forum/skins/default/templates/user_edit.html
diff --git a/forum/skins/default/templates/user_email_subscriptions.html b/forum/skins/default/templates/user_email_subscriptions.html
index c0204cbc..c0204cbc 100644..100755
--- a/forum/skins/default/templates/user_email_subscriptions.html
+++ b/forum/skins/default/templates/user_email_subscriptions.html
diff --git a/forum/skins/default/templates/user_favorites.html b/forum/skins/default/templates/user_favorites.html
index 9db01e9a..9db01e9a 100644..100755
--- a/forum/skins/default/templates/user_favorites.html
+++ b/forum/skins/default/templates/user_favorites.html
diff --git a/forum/skins/default/templates/user_footer.html b/forum/skins/default/templates/user_footer.html
index ee347742..ee347742 100644..100755
--- a/forum/skins/default/templates/user_footer.html
+++ b/forum/skins/default/templates/user_footer.html
diff --git a/forum/skins/default/templates/user_info.html b/forum/skins/default/templates/user_info.html
index c550e13f..0ce4e77a 100644..100755
--- a/forum/skins/default/templates/user_info.html
+++ b/forum/skins/default/templates/user_info.html
@@ -45,6 +45,10 @@
<span class="user-edit-link"><a href="{% url users %}{{ view_user.id }}/{% trans "edit/" %}">{% trans "update profile" %}</a></span>
{% endif %}
{% separator %}
+ {% ifequal request.user view_user %}
+ <a href="{% url auth_signin %}">add authentication method</a>
+ {% endifequal %}
+ {% separator %}
{% if request.user.has_usable_password %}
<a href="{% url user_changepw %}">change password</a>
{% endif %}
diff --git a/forum/skins/default/templates/user_recent.html b/forum/skins/default/templates/user_recent.html
index b704ab25..b704ab25 100644..100755
--- a/forum/skins/default/templates/user_recent.html
+++ b/forum/skins/default/templates/user_recent.html
diff --git a/forum/skins/default/templates/user_reputation.html b/forum/skins/default/templates/user_reputation.html
index 776935ae..776935ae 100644..100755
--- a/forum/skins/default/templates/user_reputation.html
+++ b/forum/skins/default/templates/user_reputation.html
diff --git a/forum/skins/default/templates/user_responses.html b/forum/skins/default/templates/user_responses.html
index c4f4ffed..c4f4ffed 100644..100755
--- a/forum/skins/default/templates/user_responses.html
+++ b/forum/skins/default/templates/user_responses.html
diff --git a/forum/skins/default/templates/user_stats.html b/forum/skins/default/templates/user_stats.html
index a3f88131..a3f88131 100644..100755
--- a/forum/skins/default/templates/user_stats.html
+++ b/forum/skins/default/templates/user_stats.html
diff --git a/forum/skins/default/templates/user_tabs.html b/forum/skins/default/templates/user_tabs.html
index 908e8430..908e8430 100644..100755
--- a/forum/skins/default/templates/user_tabs.html
+++ b/forum/skins/default/templates/user_tabs.html
diff --git a/forum/skins/default/templates/user_votes.html b/forum/skins/default/templates/user_votes.html
index b56aab01..b56aab01 100644..100755
--- a/forum/skins/default/templates/user_votes.html
+++ b/forum/skins/default/templates/user_votes.html
diff --git a/forum/skins/default/templates/users.html b/forum/skins/default/templates/users.html
index 3a59b0c0..3a59b0c0 100644..100755
--- a/forum/skins/default/templates/users.html
+++ b/forum/skins/default/templates/users.html
diff --git a/forum/skins/default/templates/users_questions.html b/forum/skins/default/templates/users_questions.html
index 8049d832..8049d832 100644..100755
--- a/forum/skins/default/templates/users_questions.html
+++ b/forum/skins/default/templates/users_questions.html
diff --git a/forum/templatetags/__init__.py b/forum/templatetags/__init__.py
index e69de29b..e69de29b 100644..100755
--- a/forum/templatetags/__init__.py
+++ b/forum/templatetags/__init__.py
diff --git a/forum/templatetags/extra_filters.py b/forum/templatetags/extra_filters.py
index 22ec0109..22ec0109 100644..100755
--- a/forum/templatetags/extra_filters.py
+++ b/forum/templatetags/extra_filters.py
diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py
index 26c52b8d..26c52b8d 100644..100755
--- a/forum/templatetags/extra_tags.py
+++ b/forum/templatetags/extra_tags.py
diff --git a/forum/templatetags/smart_if.py b/forum/templatetags/smart_if.py
index ca3b43fe..ca3b43fe 100644..100755
--- a/forum/templatetags/smart_if.py
+++ b/forum/templatetags/smart_if.py
diff --git a/forum/upfiles/README b/forum/upfiles/README
index 17bf8ecb..17bf8ecb 100644..100755
--- a/forum/upfiles/README
+++ b/forum/upfiles/README
diff --git a/forum/urls.py b/forum/urls.py
index fd9ebdc1..f81bad69 100644..100755
--- a/forum/urls.py
+++ b/forum/urls.py
@@ -29,7 +29,7 @@ urlpatterns = patterns('',
{'document_root': os.path.join(APP_PATH,'upfiles').replace('\\','/')},
name='uploaded_file',
),
- url(r'^%s/$' % _('signin/'), 'django_authopenid.views.signin', name='signin'),
+ #url(r'^%s/$' % _('signin/'), 'django_authopenid.views.signin', name='signin'),
url(r'^%s$' % _('about/'), app.meta.about, name='about'),
url(r'^%s$' % _('faq/'), app.meta.faq, name='faq'),
url(r'^%s$' % _('privacy/'), app.meta.privacy, name='privacy'),
@@ -86,9 +86,21 @@ urlpatterns = patterns('',
url(r'^%s$' % _('upload/'), app.writers.upload, name='upload'),
url(r'^%s$' % _('search/'), app.readers.search, name='search'),
url(r'^%s$' % _('feedback/'), app.meta.feedback, name='feedback'),
- (r'^%sfb/' % _('account/'), include('fbconnect.urls')),
- (r'^%s' % _('account/'), include('django_authopenid.urls')),
+ #(r'^%sfb/' % _('account/'), include('fbconnect.urls')),
+ #(r'^%s' % _('account/'), include('django_authopenid.urls')),
(r'^i18n/', include('django.conf.urls.i18n')),
+
+ url(r'^%s%s$' % (_('account/'), _('signin/')), app.auth.signin_page, name='auth_signin'),
+ url(r'^%s%s$' % (_('account/'), _('signout/')), app.auth.signout, name='user_signout'),
+ url(r'^%s%s(?P<action>\w+)/$' % (_('account/'), _('signin/')), app.auth.signin_page, name='auth_action_signin'),
+ url(r'^%s(?P<provider>\w+)/%s$' % (_('account/'), _('signin/')), app.auth.prepare_provider_signin, name='auth_provider_signin'),
+ url(r'^%s(?P<provider>\w+)/%s$' % (_('account/'), _('done/')), app.auth.process_provider_signin, name='auth_provider_done'),
+ url(r'^%s%s$' % (_('account/'), _('register/')), app.auth.external_register, name='auth_external_register'),
+
+ url(r'^%s%s$' % (_('account/'), _('password/')), app.users.changepw, name='user_changepw'),
+ #url(r'^%s%s%s$' % (_('accounts/'), _('password/'), _('confirm/')), app.user.confirmchangepw, name='user_confirmchangepw'),
+ url(r'^%s$' % _('account/'), app.users.account_settings, name='user_account_settings'),
+ #url(r'^%s$' % _('delete/'), app.users.delete, name='user_delete'),
)
from forum.modules import get_modules_script
diff --git a/forum/user_messages/__init__.py b/forum/user_messages/__init__.py
index 0136c888..0136c888 100644..100755
--- a/forum/user_messages/__init__.py
+++ b/forum/user_messages/__init__.py
diff --git a/forum/user_messages/context_processors.py b/forum/user_messages/context_processors.py
index 2bf26269..2bf26269 100644..100755
--- a/forum/user_messages/context_processors.py
+++ b/forum/user_messages/context_processors.py
diff --git a/forum/utils/__init__.py b/forum/utils/__init__.py
index e69de29b..e69de29b 100644..100755
--- a/forum/utils/__init__.py
+++ b/forum/utils/__init__.py
diff --git a/forum/utils/cache.py b/forum/utils/cache.py
index 410c0662..410c0662 100644..100755
--- a/forum/utils/cache.py
+++ b/forum/utils/cache.py
diff --git a/forum/utils/decorators.py b/forum/utils/decorators.py
index e4e7acb3..e4e7acb3 100644..100755
--- a/forum/utils/decorators.py
+++ b/forum/utils/decorators.py
diff --git a/forum/utils/diff.py b/forum/utils/diff.py
index d741d788..d741d788 100644..100755
--- a/forum/utils/diff.py
+++ b/forum/utils/diff.py
diff --git a/forum/utils/forms.py b/forum/utils/forms.py
index c54056ca..c54056ca 100644..100755
--- a/forum/utils/forms.py
+++ b/forum/utils/forms.py
diff --git a/forum/utils/html.py b/forum/utils/html.py
index 25a74a4a..25a74a4a 100644..100755
--- a/forum/utils/html.py
+++ b/forum/utils/html.py
diff --git a/forum/utils/lists.py b/forum/utils/lists.py
index bbcfae98..bbcfae98 100644..100755
--- a/forum/utils/lists.py
+++ b/forum/utils/lists.py
diff --git a/forum/utils/odict.py b/forum/utils/odict.py
index 2c8391d7..2c8391d7 100644..100755
--- a/forum/utils/odict.py
+++ b/forum/utils/odict.py
diff --git a/forum/views/README b/forum/views/README
index 7b6201cd..5416f88c 100644..100755
--- a/forum/views/README
+++ b/forum/views/README
@@ -9,4 +9,4 @@ users.py - user views - user listing and profiles
meta.py - privacy, about, faq, feedback, logout, badges
-books.py - book views - to be moved to a books extension
+auth.py - Authentication related views
diff --git a/forum/views/__init__.py b/forum/views/__init__.py
index 291fee2a..a5f6f99d 100644..100755
--- a/forum/views/__init__.py
+++ b/forum/views/__init__.py
@@ -3,3 +3,4 @@ import writers
import commands
import users
import meta
+import auth
diff --git a/forum/views/auth.py b/forum/views/auth.py
new file mode 100755
index 00000000..2e953163
--- /dev/null
+++ b/forum/views/auth.py
@@ -0,0 +1,212 @@
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.core.urlresolvers import reverse
+from django.contrib.auth.models import User
+from django.http import HttpResponseRedirect
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext as _
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth import login, logout
+from django.http import get_host
+import types
+
+from forum.models import AuthKeyUserAssociation
+from forum.authentication.forms import SimpleRegistrationForm, SimpleEmailSubscribeForm
+
+from forum.authentication.base import InvalidAuthentication
+from forum.authentication import AUTH_PROVIDERS
+
+from forum.models import Question, Answer
+
+def signin_page(request, action=None):
+ if action is None:
+ request.session['on_signin_url'] = request.META.get('HTTP_REFERER', '/')
+ else:
+ request.session['on_signin_action'] = action
+
+ all_providers = [provider.context for provider in AUTH_PROVIDERS.values()]
+
+ sort = lambda c1, c2: c1.weight - c2.weight
+ can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user
+
+ bigicon_providers = sorted([
+ context for context in all_providers if context.mode == 'BIGICON' and can_show(context)
+ ], sort)
+
+ smallicon_providers = sorted([
+ context for context in all_providers if context.mode == 'SMALLICON' and can_show(context)
+ ], sort)
+
+ stackitem_providers = sorted([
+ context for context in all_providers if context.mode == 'STACK_ITEM' and can_show(context)
+ ], sort)
+
+ try:
+ msg = request.session['auth_error']
+ del request.session['auth_error']
+ except:
+ msg = None
+
+ return render_to_response(
+ 'auth/signin.html',
+ {
+ 'msg': msg,
+ 'all_providers': all_providers,
+ 'bigicon_providers': bigicon_providers,
+ 'stackitem_providers': stackitem_providers,
+ 'smallicon_providers': smallicon_providers,
+ },
+ RequestContext(request))
+
+def prepare_provider_signin(request, provider):
+ force_email_request = request.REQUEST.get('validate_email', 'yes') == 'yes'
+ request.session['force_email_request'] = force_email_request
+
+ if provider in AUTH_PROVIDERS:
+ provider_class = AUTH_PROVIDERS[provider].consumer
+
+ try:
+ request_url = provider_class.prepare_authentication_request(request,
+ reverse('auth_provider_done', kwargs={'provider': provider}))
+
+ return HttpResponseRedirect(request_url)
+ except NotImplementedError, e:
+ return process_provider_signin(request, provider)
+ except InvalidAuthentication, e:
+ request.session['auth_error'] = e.message
+
+ return HttpResponseRedirect(reverse('auth_signin'))
+
+
+def process_provider_signin(request, provider):
+ if provider in AUTH_PROVIDERS:
+ provider_class = AUTH_PROVIDERS[provider].consumer
+
+ try:
+ assoc_key = provider_class.process_authentication_request(request)
+ except InvalidAuthentication, e:
+ request.session['auth_error'] = e.message
+ return HttpResponseRedirect(reverse('auth_signin'))
+
+ if request.user.is_authenticated():
+ if isinstance(assoc_key, (type, User)):
+ if request.user != assoc_key:
+ request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.")
+ else:
+ request.session['auth_error'] = _("You are already logged in with that user.")
+ else:
+ try:
+ assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
+ if assoc.user == request.user:
+ request.session['auth_error'] = _("These login credentials are already associated with your account.")
+ else:
+ request.session['auth_error'] = _("Sorry, these login credentials belong to anoother user. Plese terminate your current session and try again.")
+ except:
+ uassoc = AuthKeyUserAssociation(user=request.user, key=assoc_key, provider=provider)
+ uassoc.save()
+ request.session['auth_error'] = _("These new credentials are now associated with your account.")
+ return HttpResponseRedirect(reverse('auth_signin'))
+
+ try:
+ assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
+ user_ = assoc.user
+ return login_and_forward(request, user_)
+ except:
+ request.session['assoc_key'] = assoc_key
+ request.session['auth_provider'] = provider
+ return HttpResponseRedirect(reverse('auth_external_register'))
+
+ return HttpResponseRedirect(reverse('auth_signin'))
+
+def external_register(request):
+ if request.method == 'POST' and 'bnewaccount' in request.POST:
+ form1 = SimpleRegistrationForm(request.POST)
+ email_feeds_form = SimpleEmailSubscribeForm(request.POST)
+
+ if (form1.is_valid() and email_feeds_form.is_valid()):
+ tmp_pwd = User.objects.make_random_password()
+ user_ = User.objects.create_user(form1.cleaned_data['username'],
+ form1.cleaned_data['email'], tmp_pwd)
+
+ user_.set_unusable_password()
+
+ uassoc = AuthKeyUserAssociation(user=user_, key=request.session['assoc_key'], provider=request.session['auth_provider'])
+ uassoc.save()
+
+ email_feeds_form.save(user_)
+
+ del request.session['assoc_key']
+ del request.session['auth_provider']
+ return login_and_forward(request, user_)
+ else:
+ provider_class = AUTH_PROVIDERS[request.session['auth_provider']].consumer
+ user_data = provider_class.get_user_data(request.session['assoc_key'])
+
+ username = user_data.get('username', '')
+ email = user_data.get('email', '')
+
+ if not email:
+ email = request.session.get('auth_email_request', '')
+
+ form1 = SimpleRegistrationForm(initial={
+ 'next': '/',
+ 'username': username,
+ 'email': email,
+ })
+ email_feeds_form = SimpleEmailSubscribeForm()
+
+ provider_context = AUTH_PROVIDERS[request.session['auth_provider']].context
+
+ return render_to_response('auth/complete.html', {
+ 'form1': form1,
+ 'email_feeds_form': email_feeds_form,
+ 'provider':mark_safe(provider_context.human_name),
+ 'login_type':provider_context.id,
+ 'gravatar_faq_url':reverse('faq') + '#gravatar',
+ }, context_instance=RequestContext(request))
+
+def newquestion_signin_action(user):
+ question = Question.objects.filter(author=user).order_by('-added_at')[0]
+ return question.get_absolute_url()
+
+def newanswer_signin_action(user):
+ answer = Answer.objects.filter(author=user).order_by('-added_at')[0]
+ return answer.get_absolute_url()
+
+POST_SIGNIN_ACTIONS = {
+ 'newquestion': newquestion_signin_action,
+ 'newanswer': newanswer_signin_action,
+}
+
+def login_and_forward(request, user):
+ old_session = request.session.session_key
+ user.backend = "django.contrib.auth.backends.ModelBackend"
+ login(request, user)
+
+ from forum.models import user_logged_in
+ user_logged_in.send(user=user,session_key=old_session,sender=None)
+
+ redirect = request.session.get('on_signin_url', None)
+
+ if not redirect:
+ signin_action = request.session.get('on_signin_action', None)
+ if not signin_action:
+ redirect = reverse('index')
+ else:
+ try:
+ redirect = POST_SIGNIN_ACTIONS[signin_action](user)
+ except:
+ redirect = reverse('index')
+
+ return HttpResponseRedirect(redirect)
+
+@login_required
+def signout(request):
+ """
+ signout from the website. Remove openid from session and kill it.
+
+ url : /signout/"
+ """
+
+ logout(request)
+ return HttpResponseRedirect(reverse('index')) \ No newline at end of file
diff --git a/forum/views/commands.py b/forum/views/commands.py
index 88c2c077..88c2c077 100644..100755
--- a/forum/views/commands.py
+++ b/forum/views/commands.py
diff --git a/forum/views/meta.py b/forum/views/meta.py
index b4c7a37f..6417e8f5 100644..100755
--- a/forum/views/meta.py
+++ b/forum/views/meta.py
@@ -15,7 +15,7 @@ def about(request):
def faq(request):
data = {
'gravatar_faq_url': reverse('faq') + '#gravatar',
- 'send_email_key_url': reverse('send_email_key'),
+ #'send_email_key_url': reverse('send_email_key'),
'ask_question_url': reverse('ask'),
}
return render_to_response('faq.html', data, context_instance=RequestContext(request))
@@ -58,7 +58,7 @@ def logout(request):#refactor/change behavior?
}, context_instance=RequestContext(request))
def badges(request):#user status/reputation system
- badges = Badge.objects.all().order_by('type')
+ badges = Badge.objects.all().order_by('name')
my_badges = []
if request.user.is_authenticated():
my_badges = Award.objects.filter(user=request.user).values('badge_id')
diff --git a/forum/views/readers.py b/forum/views/readers.py
index 6b0da476..6b0da476 100644..100755
--- a/forum/views/readers.py
+++ b/forum/views/readers.py
diff --git a/forum/views/users.py b/forum/views/users.py
index cc05c19e..baa8090b 100644..100755
--- a/forum/views/users.py
+++ b/forum/views/users.py
@@ -6,11 +6,13 @@ from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
-from django.http import HttpResponse, HttpResponseForbidden
+from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect, Http404
from django.utils.translation import ugettext as _
+from django.utils.http import urlquote_plus
from django.utils.html import strip_tags
from django.core.urlresolvers import reverse
-from forum.forms import *#incomplete list is EditUserForm, ModerateUserForm, TagFilterSelectionForm,
+from forum.forms import *#incomplete list is EditUserForm, ModerateUserForm, TagFilterSelectionForm,
+from forum.utils.html import sanitize_html
from forum import auth
import calendar
from django.contrib.contenttypes.models import ContentType
@@ -98,6 +100,14 @@ def moderate_user(request, id):
response = HttpResponseForbidden(mimetype="application/json")
return response
+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)
+
@login_required
def edit_user(request, id):
user = get_object_or_404(User, id=id)
@@ -108,7 +118,6 @@ def edit_user(request, id):
if form.is_valid():
new_email = sanitize_html(form.cleaned_data['email'])
- from django_authopenid.views import set_new_email
set_new_email(user, new_email)
#user.username = sanitize_html(form.cleaned_data['username'])
@@ -945,3 +954,56 @@ def user(request, id):
func = user_view.view_func
return func(request, id, user_view)
+
+@login_required
+def changepw(request):
+ """
+ change password view.
+
+ url : /changepw/
+ template: authopenid/changepw.html
+ """
+ logging.debug('')
+ user_ = request.user
+
+ if not user_.has_usable_password():
+ raise Http404
+
+ if request.POST:
+ form = ChangePasswordForm(request.POST, user=user_)
+ if form.is_valid():
+ user_.set_password(form.cleaned_data['password1'])
+ user_.save()
+ msg = _("Password changed.")
+ redirect = "%s?msg=%s" % (
+ reverse('user_account_settings'),
+ urlquote_plus(msg))
+ return HttpResponseRedirect(redirect)
+ else:
+ form = ChangePasswordForm(user=user_)
+
+ return render_to_response('changepw.html', {'form': form },
+ context_instance=RequestContext(request))
+
+@login_required
+def account_settings(request):
+ """
+ index pages to changes some basic account settings :
+ - change password
+ - change email
+ - associate a new openid
+ - delete account
+
+ url : /
+
+ template : authopenid/settings.html
+ """
+ logging.debug('')
+ msg = request.GET.get('msg', '')
+ is_openid = False
+
+ return render_to_response('account_settings.html', {
+ 'msg': msg,
+ 'is_openid': is_openid
+ }, context_instance=RequestContext(request))
+
diff --git a/forum/views/writers.py b/forum/views/writers.py
index a8f07334..2b2461de 100644..100755
--- a/forum/views/writers.py
+++ b/forum/views/writers.py
@@ -129,7 +129,7 @@ def ask(request):#view used to ask a new question
ip_addr = request.META['REMOTE_ADDR'],
)
question.save()
- return HttpResponseRedirect(reverse('user_signin_new_question'))
+ return HttpResponseRedirect(reverse('auth_action_signin', kwargs={'action': 'newquestion'}))
else:
form = AskForm()
@@ -365,7 +365,7 @@ def answer(request, id):#process a new answer
ip_addr=request.META['REMOTE_ADDR'],
)
anon.save()
- return HttpResponseRedirect(reverse('user_signin_new_answer'))
+ return HttpResponseRedirect(reverse('auth_action_signin', kwargs={'action': 'newanswer'}))
return HttpResponseRedirect(question.get_absolute_url())
diff --git a/forum_modules/facebookauth/__init__.py b/forum_modules/facebookauth/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/forum_modules/facebookauth/__init__.py
diff --git a/forum_modules/facebookauth/authentication.py b/forum_modules/facebookauth/authentication.py
new file mode 100755
index 00000000..f2c5b6ba
--- /dev/null
+++ b/forum_modules/facebookauth/authentication.py
@@ -0,0 +1,85 @@
+import hashlib
+from time import time
+from datetime import datetime
+from urllib import urlopen, urlencode
+from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext, InvalidAuthentication
+from django.utils.translation import ugettext as _
+
+import settings
+
+try:
+ from json import load as load_json
+except:
+ from django.utils.simplejson import JSONDecoder
+
+ def load_json(json):
+ decoder = JSONDecoder()
+ return decoder.decode(json.read())
+
+class FacebookAuthConsumer(AuthenticationConsumer):
+
+ def process_authentication_request(self, request):
+ API_KEY = settings.FB_API_KEY
+
+ if API_KEY in request.COOKIES:
+ if self.check_cookies_signature(request.COOKIES):
+ if self.check_session_expiry(request.COOKIES):
+ return request.COOKIES[API_KEY + '_user']
+ else:
+ raise InvalidAuthentication(_('Sorry, your Facebook session has expired, please try again'))
+ else:
+ raise InvalidAuthentication(_('The authentication with Facebook connect failed due to an invalid signature'))
+ else:
+ raise InvalidAuthentication(_('The authentication with Facebook connect failed, cannot find authentication tokens'))
+
+ def generate_signature(self, values):
+ keys = []
+
+ for key in sorted(values.keys()):
+ keys.append(key)
+
+ signature = ''.join(['%s=%s' % (key, values[key]) for key in keys]) + settings.FB_APP_SECRET
+ return hashlib.md5(signature).hexdigest()
+
+ def check_session_expiry(self, cookies):
+ return datetime.fromtimestamp(float(cookies[settings.FB_API_KEY+'_expires'])) > datetime.now()
+
+ def check_cookies_signature(self, cookies):
+ API_KEY = settings.FB_API_KEY
+
+ values = {}
+
+ for key in cookies.keys():
+ if (key.startswith(API_KEY + '_')):
+ values[key.replace(API_KEY + '_', '')] = cookies[key]
+
+ return self.generate_signature(values) == cookies[API_KEY]
+
+ def get_user_data(self, key):
+ request_data = {
+ 'method': 'Users.getInfo',
+ 'api_key': settings.FB_API_KEY,
+ 'call_id': time(),
+ 'v': '1.0',
+ 'uids': key,
+ 'fields': 'name,first_name,last_name,email',
+ 'format': 'json',
+ }
+
+ request_data['sig'] = self.generate_signature(request_data)
+ fb_response = load_json(urlopen(settings.REST_SERVER, urlencode(request_data)))[0]
+
+ return {
+ 'username': fb_response['first_name'] + ' ' + fb_response['last_name'],
+ 'email': fb_response['email']
+ }
+
+class FacebookAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'CUSTOM'
+ weight = 100
+ human_name = 'Facebook'
+ code_template = 'modules/facebookauth/button.html'
+ extra_css = ["http://www.facebook.com/css/connect/connect_button.css"]
+
+ API_KEY = settings.FB_API_KEY \ No newline at end of file
diff --git a/forum_modules/facebookauth/settings.py b/forum_modules/facebookauth/settings.py
new file mode 100755
index 00000000..b9a01013
--- /dev/null
+++ b/forum_modules/facebookauth/settings.py
@@ -0,0 +1,3 @@
+REST_SERVER = 'http://api.facebook.com/restserver.php'
+FB_API_KEY = 'f773fab7be12aea689948208f37ad336'
+FB_APP_SECRET = '894547c1b8db54d77f919b1695ae879c' \ No newline at end of file
diff --git a/forum_modules/facebookauth/templates/button.html b/forum_modules/facebookauth/templates/button.html
new file mode 100755
index 00000000..ceae1fc2
--- /dev/null
+++ b/forum_modules/facebookauth/templates/button.html
@@ -0,0 +1,38 @@
+<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US" type="text/javascript"></script>
+<script type="text/javascript">
+ var FB_API_KEY = "{{ provider.API_KEY }}";
+ var FB_CHANNEL_PATH = "{% url xd_receiver %}";
+
+ FB.init(FB_API_KEY, FB_CHANNEL_PATH, {permsToRequestOnConnect : "email"});
+
+ function FB_ConnectPostAuthorization() {
+ if ($('#validate_email').attr('checked')) {
+ FB_RequireFeatures(["Api"], function(){
+ var api = FB.Facebook.apiClient;
+ var fb_uid = api.get_session().uid;
+
+ $.post('{% url facebook_user_is_registered %}', {'fb_uid': fb_uid}, function(response) {
+ if (response != "yes") {
+ api.users_hasAppPermission("email", function(result) {
+ if (!result) {
+ FB.Connect.showPermissionDialog("email", redirect_to_done_page);
+ } else {
+ redirect_to_done_page()
+ }
+ })
+ } else {
+ redirect_to_done_page()
+ }
+ });
+ });
+ } else {
+ redirect_to_done_page();
+ }
+ }
+
+ function redirect_to_done_page() {
+ window.location = "{% url auth_provider_done provider=provider.id %}";
+ }
+
+</script>
+<fb:login-button v="2" size="medium" onlogin="FB_ConnectPostAuthorization()">Facebook</fb:login-button> \ No newline at end of file
diff --git a/forum_modules/facebookauth/templates/xd_receiver.html b/forum_modules/facebookauth/templates/xd_receiver.html
new file mode 100755
index 00000000..9c1664d2
--- /dev/null
+++ b/forum_modules/facebookauth/templates/xd_receiver.html
@@ -0,0 +1 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <body> <script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js" type="text/javascript"></script> </body> </html>
diff --git a/forum_modules/facebookauth/urls.py b/forum_modules/facebookauth/urls.py
new file mode 100755
index 00000000..de1715c5
--- /dev/null
+++ b/forum_modules/facebookauth/urls.py
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+
+from views import user_is_registered
+
+urlpatterns = patterns('',
+ url(r'^xd_receiver.htm$', direct_to_template, {'template': 'modules/facebookauth/xd_receiver.html'}, name='xd_receiver'),
+ url(r'^facebook/user_is_registered/', user_is_registered, name="facebook_user_is_registered"),
+) \ No newline at end of file
diff --git a/forum_modules/facebookauth/views.py b/forum_modules/facebookauth/views.py
new file mode 100755
index 00000000..f1e8f3be
--- /dev/null
+++ b/forum_modules/facebookauth/views.py
@@ -0,0 +1,11 @@
+from forum.models import AuthKeyUserAssociation
+from django.http import HttpResponse
+
+def user_is_registered(request):
+ try:
+ fb_uid = request.POST['fb_uid']
+ print fb_uid
+ AuthKeyUserAssociation.objects.get(key=fb_uid)
+ return HttpResponse('yes')
+ except:
+ return HttpResponse('no') \ No newline at end of file
diff --git a/forum_modules/localauth/__init__.py b/forum_modules/localauth/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/forum_modules/localauth/__init__.py
diff --git a/forum_modules/localauth/authentication.py b/forum_modules/localauth/authentication.py
new file mode 100755
index 00000000..3169a99c
--- /dev/null
+++ b/forum_modules/localauth/authentication.py
@@ -0,0 +1,18 @@
+from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext, InvalidAuthentication
+from forms import ClassicLoginForm
+
+class LocalAuthConsumer(AuthenticationConsumer):
+ def process_authentication_request(self, request):
+ form_auth = ClassicLoginForm(request.POST)
+
+ if form_auth.is_valid():
+ return form_auth.get_user()
+ else:
+ raise InvalidAuthentication(" ".join(form_auth.errors.values()[0]))
+
+class LocalAuthContext(ConsumerTemplateContext):
+ mode = 'STACK_ITEM'
+ weight = 1000
+ human_name = 'Local authentication'
+ stack_item_template = 'modules/localauth/loginform.html'
+ show_to_logged_in_user = False \ No newline at end of file
diff --git a/forum_modules/localauth/forms.py b/forum_modules/localauth/forms.py
new file mode 100755
index 00000000..59f5d565
--- /dev/null
+++ b/forum_modules/localauth/forms.py
@@ -0,0 +1,77 @@
+from forum.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
+from forum.models import EmailFeedSetting, Question
+from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext as _
+from django.contrib.auth import authenticate
+from django import forms
+import logging
+
+class ClassicRegisterForm(SetPasswordForm):
+ """ legacy registration form """
+
+ next = NextUrlField()
+ username = UserNameField()
+ email = UserEmailField()
+ #fields password1 and password2 are inherited
+ #recaptcha = ReCaptchaField()
+
+class ClassicLoginForm(forms.Form):
+ """ legacy account signin form """
+ next = NextUrlField()
+ username = UserNameField(required=False,skip_clean=True)
+ password = forms.CharField(max_length=128,
+ widget=forms.widgets.PasswordInput(attrs={'class':'required login'}),
+ required=False)
+
+ def __init__(self, data=None, files=None, auto_id='id_%s',
+ prefix=None, initial=None):
+ super(ClassicLoginForm, self).__init__(data, files, auto_id,
+ prefix, initial)
+ self.user_cache = None
+
+ def _clean_nonempty_field(self,field):
+ value = None
+ if field in self.cleaned_data:
+ value = str(self.cleaned_data[field]).strip()
+ if value == '':
+ value = None
+ self.cleaned_data[field] = value
+ return value
+
+ def clean_username(self):
+ return self._clean_nonempty_field('username')
+
+ def clean_password(self):
+ return self._clean_nonempty_field('password')
+
+ def clean(self):
+ error_list = []
+ username = self.cleaned_data['username']
+ password = self.cleaned_data['password']
+
+ self.user_cache = None
+ if username and password:
+ self.user_cache = authenticate(username=username, password=password)
+
+ if self.user_cache is None:
+ del self.cleaned_data['username']
+ del self.cleaned_data['password']
+ error_list.insert(0,(_("Please enter valid username and password "
+ "(both are case-sensitive).")))
+ elif self.user_cache.is_active == False:
+ error_list.append(_("This account is inactive."))
+ if len(error_list) > 0:
+ error_list.insert(0,_('Login failed.'))
+ elif password == None and username == None:
+ error_list.append(_('Please enter username and password'))
+ elif password == None:
+ error_list.append(_('Please enter your password'))
+ elif username == None:
+ error_list.append(_('Please enter user name'))
+ if len(error_list) > 0:
+ self._errors['__all__'] = forms.util.ErrorList(error_list)
+ return self.cleaned_data
+
+ def get_user(self):
+ """ get authenticated user """
+ return self.user_cache \ No newline at end of file
diff --git a/forum_modules/localauth/templates/loginform.html b/forum_modules/localauth/templates/loginform.html
new file mode 100755
index 00000000..0d95a2f2
--- /dev/null
+++ b/forum_modules/localauth/templates/loginform.html
@@ -0,0 +1,31 @@
+{% load i18n %}
+
+<fieldset id='local_login_fs'>
+ <p><span class='big strong'>Enter your local user name and password</span><br/><span class='grey'>(or select your external provider above)</span></p>
+ <table>
+ <tr>
+ <td>
+ <label for="id_username">Login name</label>
+ </td>
+ <td>
+ <input id="id_username" type="text" class="required login" name="username" maxlength="30" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <label for="id_password">Password</label>
+ </td>
+ <td>
+ <input id="id_password" type="password" class="required login" name="password" maxlength="128" />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <input id="blogin" name="blogin" type="submit" value="Login" />
+ </td>
+ <td>
+ <a href="{% url auth_local_register %}">Create account</a><span>&nbsp;|&nbsp;</span><a href="{% url auth_local_register %}">Forgot your password?</a>
+ </td>
+ </tr>
+ </table>
+</fieldset> \ No newline at end of file
diff --git a/forum_modules/localauth/urls.py b/forum_modules/localauth/urls.py
new file mode 100755
index 00000000..e2a3b263
--- /dev/null
+++ b/forum_modules/localauth/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls.defaults import *
+from django.views.generic.simple import direct_to_template
+from django.utils.translation import ugettext as _
+import views as app
+
+urlpatterns = patterns('',
+ url(r'^%s%s%s$' % (_('account/'), _('local/'), _('register/')), app.register, name='auth_local_register'),
+) \ No newline at end of file
diff --git a/forum_modules/localauth/views.py b/forum_modules/localauth/views.py
new file mode 100755
index 00000000..acad16cf
--- /dev/null
+++ b/forum_modules/localauth/views.py
@@ -0,0 +1,30 @@
+from django.contrib.auth.models import User
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+
+from forms import ClassicRegisterForm
+from forum.authentication.forms import SimpleEmailSubscribeForm
+from forum.views.auth import login_and_forward
+
+def register(request):
+ if request.method == 'POST':
+ form = ClassicRegisterForm(request.POST)
+ email_feeds_form = SimpleEmailSubscribeForm(request.POST)
+
+ if form.is_valid() and email_feeds_form.is_valid():
+ username = form.cleaned_data['username']
+ password = form.cleaned_data['password1']
+ email = form.cleaned_data['email']
+
+ user_ = User.objects.create_user( username,email,password )
+ email_feeds_form.save(user_)
+ #todo: email validation
+ return login_and_forward(request, user_)
+ else:
+ form = ClassicRegisterForm(initial={'next':'/'})
+ email_feeds_form = SimpleEmailSubscribeForm()
+
+ return render_to_response('auth/signup.html', {
+ 'form': form,
+ 'email_feeds_form': email_feeds_form
+ }, context_instance=RequestContext(request)) \ No newline at end of file
diff --git a/forum_modules/oauthauth/__init__.py b/forum_modules/oauthauth/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/forum_modules/oauthauth/__init__.py
diff --git a/forum_modules/oauthauth/authentication.py b/forum_modules/oauthauth/authentication.py
new file mode 100755
index 00000000..cfe81568
--- /dev/null
+++ b/forum_modules/oauthauth/authentication.py
@@ -0,0 +1,41 @@
+from consumer import OAuthAbstractAuthConsumer
+from forum.authentication.base import ConsumerTemplateContext
+
+try:
+ import json as simplejson
+except ImportError:
+ from django.utils import simplejson
+
+from lib import oauth
+import settings
+
+class TwitterAuthConsumer(OAuthAbstractAuthConsumer):
+ def __init__(self):
+ OAuthAbstractAuthConsumer.__init__(self,
+ settings.TWITTER_CONSUMER_KEY,
+ settings.TWITTER_CONSUMER_SECRET,
+ "twitter.com",
+ "https://twitter.com/oauth/request_token",
+ "https://twitter.com/oauth/access_token",
+ "https://twitter.com/oauth/authorize",
+ )
+
+ def get_user_data(self, key):
+ json = self.fetch_data(key, "https://twitter.com/account/verify_credentials.json")
+
+ if 'screen_name' in json:
+ creds = simplejson.loads(json)
+
+ return {
+ 'username': creds['screen_name']
+ }
+
+
+ return {}
+
+class TwitterAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'DIRECT'
+ weight = 150
+ human_name = 'Twitter'
+ icon = '/media/images/openid/twitter.png' \ No newline at end of file
diff --git a/forum_modules/oauthauth/consumer.py b/forum_modules/oauthauth/consumer.py
new file mode 100755
index 00000000..e3cbda6c
--- /dev/null
+++ b/forum_modules/oauthauth/consumer.py
@@ -0,0 +1,87 @@
+import urllib
+import urllib2
+import httplib
+import time
+
+from forum.authentication.base import AuthenticationConsumer, InvalidAuthentication
+from django.utils.translation import ugettext as _
+
+from lib import oauth
+
+class OAuthAbstractAuthConsumer(AuthenticationConsumer):
+
+ def __init__(self, consumer_key, consumer_secret, server_url, request_token_url, access_token_url, authorization_url):
+ self.consumer_secret = consumer_secret
+ self.consumer_key = consumer_key
+
+ self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
+ self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
+
+ self.server_url = server_url
+ self.request_token_url = request_token_url
+ self.access_token_url = access_token_url
+ self.authorization_url = authorization_url
+
+ def prepare_authentication_request(self, request, redirect_to):
+ request_token = self.fetch_request_token()
+ request.session['unauthed_token'] = request_token.to_string()
+ return self.authorize_token_url(request_token)
+
+ def process_authentication_request(self, request):
+ unauthed_token = request.session.get('unauthed_token', None)
+ if not unauthed_token:
+ raise InvalidAuthentication(_('Error, the oauth token is not on the server'))
+
+ token = oauth.OAuthToken.from_string(unauthed_token)
+
+ if token.key != request.GET.get('oauth_token', 'no-token'):
+ raise InvalidAuthentication(_("Something went wrong! Auth tokens do not match"))
+
+ access_token = self.fetch_access_token(token)
+
+ return access_token.to_string()
+
+ def get_user_data(self, key):
+ #token = oauth.OAuthToken.from_string(access_token)
+ return {}
+
+ def fetch_request_token(self):
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url)
+ oauth_request.sign_request(self.signature_method, self.consumer, None)
+ params = oauth_request.parameters
+ data = urllib.urlencode(params)
+ full_url='%s?%s'%(self.request_token_url, data)
+ response = urllib2.urlopen(full_url)
+ return oauth.OAuthToken.from_string(response.read())
+
+ def authorize_token_url(self, token, callback_url=None):
+ oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token,\
+ callback=callback_url, http_url=self.authorization_url)
+ params = oauth_request.parameters
+ data = urllib.urlencode(params)
+ full_url='%s?%s'%(self.authorization_url, data)
+ return full_url
+
+ def fetch_access_token(self, token):
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=token, http_url=self.access_token_url)
+ oauth_request.sign_request(self.signature_method, self.consumer, token)
+ params = oauth_request.parameters
+ data = urllib.urlencode(params)
+ full_url='%s?%s'%(self.access_token_url, data)
+ response = urllib2.urlopen(full_url)
+ return oauth.OAuthToken.from_string(response.read())
+
+ def fetch_data(self, token, http_url, parameters=None):
+ access_token = oauth.OAuthToken.from_string(token)
+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(
+ self.consumer, token=access_token, http_method="GET",
+ http_url=http_url, parameters=parameters,
+ )
+ oauth_request.sign_request(self.signature_method, self.consumer, access_token)
+
+ url = oauth_request.to_url()
+ connection = httplib.HTTPSConnection(self.server_url)
+ connection.request(oauth_request.http_method, url)
+
+ return connection.getresponse().read()
+
diff --git a/forum_modules/oauthauth/lib/__init__.py b/forum_modules/oauthauth/lib/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/forum_modules/oauthauth/lib/__init__.py
diff --git a/forum_modules/oauthauth/lib/oauth.py b/forum_modules/oauthauth/lib/oauth.py
new file mode 100755
index 00000000..e2af5c0f
--- /dev/null
+++ b/forum_modules/oauthauth/lib/oauth.py
@@ -0,0 +1,594 @@
+"""
+The MIT License
+
+Copyright (c) 2007 Leah Culver
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+import cgi
+import urllib
+import time
+import random
+import urlparse
+import hmac
+import binascii
+
+
+VERSION = '1.0' # Hi Blaine!
+HTTP_METHOD = 'GET'
+SIGNATURE_METHOD = 'PLAINTEXT'
+
+
+class OAuthError(RuntimeError):
+ """Generic exception class."""
+ def __init__(self, message='OAuth error occured.'):
+ self.message = message
+
+def build_authenticate_header(realm=''):
+ """Optional WWW-Authenticate header (401 error)"""
+ return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
+
+def escape(s):
+ """Escape a URL including any /."""
+ return urllib.quote(s, safe='~')
+
+def _utf8_str(s):
+ """Convert unicode to utf-8."""
+ if isinstance(s, unicode):
+ return s.encode("utf-8")
+ else:
+ return str(s)
+
+def generate_timestamp():
+ """Get seconds since epoch (UTC)."""
+ return int(time.time())
+
+def generate_nonce(length=8):
+ """Generate pseudorandom number."""
+ return ''.join([str(random.randint(0, 9)) for i in range(length)])
+
+
+class OAuthConsumer(object):
+ """Consumer of OAuth authentication.
+
+ OAuthConsumer is a data type that represents the identity of the Consumer
+ via its shared secret with the Service Provider.
+
+ """
+ key = None
+ secret = None
+
+ def __init__(self, key, secret):
+ self.key = key
+ self.secret = secret
+
+
+class OAuthToken(object):
+ """OAuthToken is a data type that represents an End User via either an access
+ or request token.
+
+ key -- the token
+ secret -- the token secret
+
+ """
+ key = None
+ secret = None
+
+ def __init__(self, key, secret):
+ self.key = key
+ self.secret = secret
+
+ def to_string(self):
+ return urllib.urlencode({'oauth_token': self.key,
+ 'oauth_token_secret': self.secret})
+
+ def from_string(s):
+ """ Returns a token from something like:
+ oauth_token_secret=xxx&oauth_token=xxx
+ """
+ params = cgi.parse_qs(s, keep_blank_values=False)
+ key = params['oauth_token'][0]
+ secret = params['oauth_token_secret'][0]
+ return OAuthToken(key, secret)
+ from_string = staticmethod(from_string)
+
+ def __str__(self):
+ return self.to_string()
+
+
+class OAuthRequest(object):
+ """OAuthRequest represents the request and can be serialized.
+
+ OAuth parameters:
+ - oauth_consumer_key
+ - oauth_token
+ - oauth_signature_method
+ - oauth_signature
+ - oauth_timestamp
+ - oauth_nonce
+ - oauth_version
+ ... any additional parameters, as defined by the Service Provider.
+ """
+ parameters = None # OAuth parameters.
+ http_method = HTTP_METHOD
+ http_url = None
+ version = VERSION
+
+ def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None):
+ self.http_method = http_method
+ self.http_url = http_url
+ self.parameters = parameters or {}
+
+ def set_parameter(self, parameter, value):
+ self.parameters[parameter] = value
+
+ def get_parameter(self, parameter):
+ try:
+ return self.parameters[parameter]
+ except:
+ raise OAuthError('Parameter not found: %s' % parameter)
+
+ def _get_timestamp_nonce(self):
+ return self.get_parameter('oauth_timestamp'), self.get_parameter(
+ 'oauth_nonce')
+
+ def get_nonoauth_parameters(self):
+ """Get any non-OAuth parameters."""
+ parameters = {}
+ for k, v in self.parameters.iteritems():
+ # Ignore oauth parameters.
+ if k.find('oauth_') < 0:
+ parameters[k] = v
+ return parameters
+
+ def to_header(self, realm=''):
+ """Serialize as a header for an HTTPAuth request."""
+ auth_header = 'OAuth realm="%s"' % realm
+ # Add the oauth parameters.
+ if self.parameters:
+ for k, v in self.parameters.iteritems():
+ if k[:6] == 'oauth_':
+ auth_header += ', %s="%s"' % (k, escape(str(v)))
+ return {'Authorization': auth_header}
+
+ def to_postdata(self):
+ """Serialize as post data for a POST request."""
+ return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \
+ for k, v in self.parameters.iteritems()])
+
+ def to_url(self):
+ """Serialize as a URL for a GET request."""
+ return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
+
+ def get_normalized_parameters(self):
+ """Return a string that contains the parameters that must be signed."""
+ params = self.parameters
+ try:
+ # Exclude the signature if it exists.
+ del params['oauth_signature']
+ except:
+ pass
+ # Escape key values before sorting.
+ key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \
+ for k,v in params.items()]
+ # Sort lexicographically, first after key, then after value.
+ key_values.sort()
+ # Combine key value pairs into a string.
+ return '&'.join(['%s=%s' % (k, v) for k, v in key_values])
+
+ def get_normalized_http_method(self):
+ """Uppercases the http method."""
+ return self.http_method.upper()
+
+ def get_normalized_http_url(self):
+ """Parses the URL and rebuilds it to be scheme://host/path."""
+ parts = urlparse.urlparse(self.http_url)
+ scheme, netloc, path = parts[:3]
+ # Exclude default port numbers.
+ if scheme == 'http' and netloc[-3:] == ':80':
+ netloc = netloc[:-3]
+ elif scheme == 'https' and netloc[-4:] == ':443':
+ netloc = netloc[:-4]
+ return '%s://%s%s' % (scheme, netloc, path)
+
+ def sign_request(self, signature_method, consumer, token):
+ """Set the signature parameter to the result of build_signature."""
+ # Set the signature method.
+ self.set_parameter('oauth_signature_method',
+ signature_method.get_name())
+ # Set the signature.
+ self.set_parameter('oauth_signature',
+ self.build_signature(signature_method, consumer, token))
+
+ def build_signature(self, signature_method, consumer, token):
+ """Calls the build signature method within the signature method."""
+ return signature_method.build_signature(self, consumer, token)
+
+ def from_request(http_method, http_url, headers=None, parameters=None,
+ query_string=None):
+ """Combines multiple parameter sources."""
+ if parameters is None:
+ parameters = {}
+
+ # Headers
+ if headers and 'Authorization' in headers:
+ auth_header = headers['Authorization']
+ # Check that the authorization header is OAuth.
+ if auth_header.index('OAuth') > -1:
+ auth_header = auth_header.lstrip('OAuth ')
+ try:
+ # Get the parameters from the header.
+ header_params = OAuthRequest._split_header(auth_header)
+ parameters.update(header_params)
+ except:
+ raise OAuthError('Unable to parse OAuth parameters from '
+ 'Authorization header.')
+
+ # GET or POST query string.
+ if query_string:
+ query_params = OAuthRequest._split_url_string(query_string)
+ parameters.update(query_params)
+
+ # URL parameters.
+ param_str = urlparse.urlparse(http_url)[4] # query
+ url_params = OAuthRequest._split_url_string(param_str)
+ parameters.update(url_params)
+
+ if parameters:
+ return OAuthRequest(http_method, http_url, parameters)
+
+ return None
+ from_request = staticmethod(from_request)
+
+ def from_consumer_and_token(oauth_consumer, token=None,
+ http_method=HTTP_METHOD, http_url=None, parameters=None):
+ if not parameters:
+ parameters = {}
+
+ defaults = {
+ 'oauth_consumer_key': oauth_consumer.key,
+ 'oauth_timestamp': generate_timestamp(),
+ 'oauth_nonce': generate_nonce(),
+ 'oauth_version': OAuthRequest.version,
+ }
+
+ defaults.update(parameters)
+ parameters = defaults
+
+ if token:
+ parameters['oauth_token'] = token.key
+
+ return OAuthRequest(http_method, http_url, parameters)
+ from_consumer_and_token = staticmethod(from_consumer_and_token)
+
+ def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD,
+ http_url=None, parameters=None):
+ if not parameters:
+ parameters = {}
+
+ parameters['oauth_token'] = token.key
+
+ if callback:
+ parameters['oauth_callback'] = callback
+
+ return OAuthRequest(http_method, http_url, parameters)
+ from_token_and_callback = staticmethod(from_token_and_callback)
+
+ def _split_header(header):
+ """Turn Authorization: header into parameters."""
+ params = {}
+ parts = header.split(',')
+ for param in parts:
+ # Ignore realm parameter.
+ if param.find('realm') > -1:
+ continue
+ # Remove whitespace.
+ param = param.strip()
+ # Split key-value.
+ param_parts = param.split('=', 1)
+ # Remove quotes and unescape the value.
+ params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
+ return params
+ _split_header = staticmethod(_split_header)
+
+ def _split_url_string(param_str):
+ """Turn URL string into parameters."""
+ parameters = cgi.parse_qs(param_str, keep_blank_values=False)
+ for k, v in parameters.iteritems():
+ parameters[k] = urllib.unquote(v[0])
+ return parameters
+ _split_url_string = staticmethod(_split_url_string)
+
+class OAuthServer(object):
+ """A worker to check the validity of a request against a data store."""
+ timestamp_threshold = 300 # In seconds, five minutes.
+ version = VERSION
+ signature_methods = None
+ data_store = None
+
+ def __init__(self, data_store=None, signature_methods=None):
+ self.data_store = data_store
+ self.signature_methods = signature_methods or {}
+
+ def set_data_store(self, data_store):
+ self.data_store = data_store
+
+ def get_data_store(self):
+ return self.data_store
+
+ def add_signature_method(self, signature_method):
+ self.signature_methods[signature_method.get_name()] = signature_method
+ return self.signature_methods
+
+ def fetch_request_token(self, oauth_request):
+ """Processes a request_token request and returns the
+ request token on success.
+ """
+ try:
+ # Get the request token for authorization.
+ token = self._get_token(oauth_request, 'request')
+ except OAuthError:
+ # No token required for the initial token request.
+ version = self._get_version(oauth_request)
+ consumer = self._get_consumer(oauth_request)
+ self._check_signature(oauth_request, consumer, None)
+ # Fetch a new token.
+ token = self.data_store.fetch_request_token(consumer)
+ return token
+
+ def fetch_access_token(self, oauth_request):
+ """Processes an access_token request and returns the
+ access token on success.
+ """
+ version = self._get_version(oauth_request)
+ consumer = self._get_consumer(oauth_request)
+ # Get the request token.
+ token = self._get_token(oauth_request, 'request')
+ self._check_signature(oauth_request, consumer, token)
+ new_token = self.data_store.fetch_access_token(consumer, token)
+ return new_token
+
+ def verify_request(self, oauth_request):
+ """Verifies an api call and checks all the parameters."""
+ # -> consumer and token
+ version = self._get_version(oauth_request)
+ consumer = self._get_consumer(oauth_request)
+ # Get the access token.
+ token = self._get_token(oauth_request, 'access')
+ self._check_signature(oauth_request, consumer, token)
+ parameters = oauth_request.get_nonoauth_parameters()
+ return consumer, token, parameters
+
+ def authorize_token(self, token, user):
+ """Authorize a request token."""
+ return self.data_store.authorize_request_token(token, user)
+
+ def get_callback(self, oauth_request):
+ """Get the callback URL."""
+ return oauth_request.get_parameter('oauth_callback')
+
+ def build_authenticate_header(self, realm=''):
+ """Optional support for the authenticate header."""
+ return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
+
+ def _get_version(self, oauth_request):
+ """Verify the correct version request for this server."""
+ try:
+ version = oauth_request.get_parameter('oauth_version')
+ except:
+ version = VERSION
+ if version and version != self.version:
+ raise OAuthError('OAuth version %s not supported.' % str(version))
+ return version
+
+ def _get_signature_method(self, oauth_request):
+ """Figure out the signature with some defaults."""
+ try:
+ signature_method = oauth_request.get_parameter(
+ 'oauth_signature_method')
+ except:
+ signature_method = SIGNATURE_METHOD
+ try:
+ # Get the signature method object.
+ signature_method = self.signature_methods[signature_method]
+ except:
+ signature_method_names = ', '.join(self.signature_methods.keys())
+ raise OAuthError('Signature method %s not supported try one of the '
+ 'following: %s' % (signature_method, signature_method_names))
+
+ return signature_method
+
+ def _get_consumer(self, oauth_request):
+ consumer_key = oauth_request.get_parameter('oauth_consumer_key')
+ consumer = self.data_store.lookup_consumer(consumer_key)
+ if not consumer:
+ raise OAuthError('Invalid consumer.')
+ return consumer
+
+ def _get_token(self, oauth_request, token_type='access'):
+ """Try to find the token for the provided request token key."""
+ token_field = oauth_request.get_parameter('oauth_token')
+ token = self.data_store.lookup_token(token_type, token_field)
+ if not token:
+ raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
+ return token
+
+ def _check_signature(self, oauth_request, consumer, token):
+ timestamp, nonce = oauth_request._get_timestamp_nonce()
+ self._check_timestamp(timestamp)
+ self._check_nonce(consumer, token, nonce)
+ signature_method = self._get_signature_method(oauth_request)
+ try:
+ signature = oauth_request.get_parameter('oauth_signature')
+ except:
+ raise OAuthError('Missing signature.')
+ # Validate the signature.
+ valid_sig = signature_method.check_signature(oauth_request, consumer,
+ token, signature)
+ if not valid_sig:
+ key, base = signature_method.build_signature_base_string(
+ oauth_request, consumer, token)
+ raise OAuthError('Invalid signature. Expected signature base '
+ 'string: %s' % base)
+ built = signature_method.build_signature(oauth_request, consumer, token)
+
+ def _check_timestamp(self, timestamp):
+ """Verify that timestamp is recentish."""
+ timestamp = int(timestamp)
+ now = int(time.time())
+ lapsed = now - timestamp
+ if lapsed > self.timestamp_threshold:
+ raise OAuthError('Expired timestamp: given %d and now %s has a '
+ 'greater difference than threshold %d' %
+ (timestamp, now, self.timestamp_threshold))
+
+ def _check_nonce(self, consumer, token, nonce):
+ """Verify that the nonce is uniqueish."""
+ nonce = self.data_store.lookup_nonce(consumer, token, nonce)
+ if nonce:
+ raise OAuthError('Nonce already used: %s' % str(nonce))
+
+
+class OAuthClient(object):
+ """OAuthClient is a worker to attempt to execute a request."""
+ consumer = None
+ token = None
+
+ def __init__(self, oauth_consumer, oauth_token):
+ self.consumer = oauth_consumer
+ self.token = oauth_token
+
+ def get_consumer(self):
+ return self.consumer
+
+ def get_token(self):
+ return self.token
+
+ def fetch_request_token(self, oauth_request):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def fetch_access_token(self, oauth_request):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def access_resource(self, oauth_request):
+ """-> Some protected resource."""
+ raise NotImplementedError
+
+
+class OAuthDataStore(object):
+ """A database abstraction used to lookup consumers and tokens."""
+
+ def lookup_consumer(self, key):
+ """-> OAuthConsumer."""
+ raise NotImplementedError
+
+ def lookup_token(self, oauth_consumer, token_type, token_token):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def fetch_request_token(self, oauth_consumer):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def fetch_access_token(self, oauth_consumer, oauth_token):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+ def authorize_request_token(self, oauth_token, user):
+ """-> OAuthToken."""
+ raise NotImplementedError
+
+
+class OAuthSignatureMethod(object):
+ """A strategy class that implements a signature method."""
+ def get_name(self):
+ """-> str."""
+ raise NotImplementedError
+
+ def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
+ """-> str key, str raw."""
+ raise NotImplementedError
+
+ def build_signature(self, oauth_request, oauth_consumer, oauth_token):
+ """-> str."""
+ raise NotImplementedError
+
+ def check_signature(self, oauth_request, consumer, token, signature):
+ built = self.build_signature(oauth_request, consumer, token)
+ return built == signature
+
+
+class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
+
+ def get_name(self):
+ return 'HMAC-SHA1'
+
+ def build_signature_base_string(self, oauth_request, consumer, token):
+ sig = (
+ escape(oauth_request.get_normalized_http_method()),
+ escape(oauth_request.get_normalized_http_url()),
+ escape(oauth_request.get_normalized_parameters()),
+ )
+
+ key = '%s&' % escape(consumer.secret)
+ if token:
+ key += escape(token.secret)
+ raw = '&'.join(sig)
+ return key, raw
+
+ def build_signature(self, oauth_request, consumer, token):
+ """Builds the base signature string."""
+ key, raw = self.build_signature_base_string(oauth_request, consumer,
+ token)
+
+ # HMAC object.
+ try:
+ import hashlib # 2.5
+ hashed = hmac.new(key, raw, hashlib.sha1)
+ except:
+ import sha # Deprecated
+ hashed = hmac.new(key, raw, sha)
+
+ # Calculate the digest base 64.
+ return binascii.b2a_base64(hashed.digest())[:-1]
+
+
+class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
+
+ def get_name(self):
+ return 'PLAINTEXT'
+
+ def build_signature_base_string(self, oauth_request, consumer, token):
+ """Concatenates the consumer key and secret."""
+ sig = '%s&' % escape(consumer.secret)
+ if token:
+ sig = sig + escape(token.secret)
+ return sig, sig
+
+ def build_signature(self, oauth_request, consumer, token):
+ key, raw = self.build_signature_base_string(oauth_request, consumer,
+ token)
+ return key \ No newline at end of file
diff --git a/forum_modules/oauthauth/settings.py b/forum_modules/oauthauth/settings.py
new file mode 100755
index 00000000..d503fef6
--- /dev/null
+++ b/forum_modules/oauthauth/settings.py
@@ -0,0 +1,3 @@
+TWITTER_CONSUMER_KEY = "sAAGwWILliIbgbrG37GztQ"
+TWITTER_CONSUMER_SECRET = "AZv0pHTZQaf4rxxZOrj3Jm1RKgmlV4MnYJAsrY7M0"
+
diff --git a/forum_modules/openidauth/__init__.py b/forum_modules/openidauth/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/forum_modules/openidauth/__init__.py
diff --git a/forum_modules/openidauth/authentication.py b/forum_modules/openidauth/authentication.py
new file mode 100755
index 00000000..d34591e2
--- /dev/null
+++ b/forum_modules/openidauth/authentication.py
@@ -0,0 +1,196 @@
+from consumer import OpenIdAbstractAuthConsumer
+from forum.authentication.base import ConsumerTemplateContext
+
+class GoogleAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ return 'https://www.google.com/accounts/o8/id'
+
+class GoogleAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'DIRECT'
+ weight = 200
+ human_name = 'Google'
+ icon = '/media/images/openid/google.gif'
+
+
+
+class YahooAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ return 'http://yahoo.com/'
+
+class YahooAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'DIRECT'
+ weight = 300
+ human_name = 'Yahoo'
+ icon = '/media/images/openid/yahoo.gif'
+
+
+
+class AolAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ uname = request.POST['input_field']
+ return 'http://openid.aol.com/' + uname
+
+class AolAuthContext(ConsumerTemplateContext):
+ mode = 'BIGICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'AOL screen name'
+ }
+ weight = 400
+ human_name = 'AOL'
+ icon = '/media/images/openid/aol.gif'
+
+
+class MyOpenIdAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.myopenid.com/" % blog_name
+
+class MyOpenIdAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'MyOpenID user name'
+ }
+ weight = 200
+ human_name = 'MyOpenID'
+ icon = '/media/images/openid/myopenid.ico'
+
+
+class FlickrAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://flickr.com/%s/" % blog_name
+
+class FlickrAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Flickr user name'
+ }
+ weight = 250
+ human_name = 'Flickr'
+ icon = '/media/images/openid/flickr.ico'
+
+
+class TechnoratiAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://technorati.com/people/technorati/%s/" % blog_name
+
+class TechnoratiAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Technorati user name'
+ }
+ weight = 260
+ human_name = 'Technorati'
+ icon = '/media/images/openid/technorati.ico'
+
+
+class WordpressAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.wordpress.com/" % blog_name
+
+class WordpressAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Wordpress blog name'
+ }
+ weight = 270
+ human_name = 'Wordpress'
+ icon = '/media/images/openid/wordpress.ico'
+
+
+class BloggerAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.blogspot.com/" % blog_name
+
+class BloggerAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Blogger blog name'
+ }
+ weight = 300
+ human_name = 'Blogger'
+ icon = '/media/images/openid/blogger.ico'
+
+
+class LiveJournalAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.livejournal.com/" % blog_name
+
+class LiveJournalAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'LiveJournal blog name'
+ }
+ weight = 310
+ human_name = 'LiveJournal'
+ icon = '/media/images/openid/livejournal.ico'
+
+
+class ClaimIdAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://claimid.com/%s" % blog_name
+
+class ClaimIdAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'ClaimID user name'
+ }
+ weight = 320
+ human_name = 'ClaimID'
+ icon = '/media/images/openid/claimid.ico'
+
+class VidoopAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.myvidoop.com/" % blog_name
+
+class VidoopAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Vidoop user name'
+ }
+ weight = 330
+ human_name = 'Vidoop'
+ icon = '/media/images/openid/vidoop.ico'
+
+class VerisignAuthConsumer(OpenIdAbstractAuthConsumer):
+ def get_user_url(self, request):
+ blog_name = request.POST['input_field']
+ return "http://%s.pip.verisignlabs.com/" % blog_name
+
+class VerisignAuthContext(ConsumerTemplateContext):
+ mode = 'SMALLICON'
+ type = 'SIMPLE_FORM'
+ simple_form_context = {
+ 'your_what': 'Verisign user name'
+ }
+ weight = 340
+ human_name = 'Verisign'
+ icon = '/media/images/openid/verisign.ico'
+
+
+class OpenIdUrlAuthConsumer(OpenIdAbstractAuthConsumer):
+ pass
+
+class OpenIdUrlAuthContext(ConsumerTemplateContext):
+ mode = 'STACK_ITEM'
+ weight = 300
+ human_name = 'OpenId url'
+ stack_item_template = 'modules/openidauth/openidurl.html'
+ icon = '/media/images/openid/openid-inputicon.gif' \ No newline at end of file
diff --git a/forum_modules/openidauth/consumer.py b/forum_modules/openidauth/consumer.py
new file mode 100755
index 00000000..17d1378f
--- /dev/null
+++ b/forum_modules/openidauth/consumer.py
@@ -0,0 +1,112 @@
+from django.utils.html import escape
+from django.http import get_host
+
+from forum.authentication.base import AuthenticationConsumer, InvalidAuthentication
+import settings
+
+from openid.yadis import xri
+from openid.consumer.consumer import Consumer, SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
+from openid.consumer.discover import DiscoveryFailure
+from openid.extensions.sreg import SRegRequest, SRegResponse
+from openid.extensions.ax import FetchRequest as AXFetchRequest, AttrInfo, FetchResponse as AXFetchResponse
+from django.utils.translation import ugettext as _
+
+from store import OsqaOpenIDStore
+
+class OpenIdAbstractAuthConsumer(AuthenticationConsumer):
+
+ def get_user_url(self, request):
+ try:
+ return request.POST['openid_identifier']
+ except:
+ raise NotImplementedError()
+
+ def prepare_authentication_request(self, request, redirect_to):
+ if not redirect_to.startswith('http://') or redirect_to.startswith('https://'):
+ redirect_to = get_url_host(request) + redirect_to
+
+ user_url = self.get_user_url(request)
+
+ if xri.identifierScheme(user_url) == 'XRI' and getattr(
+ settings, 'OPENID_DISALLOW_INAMES', False
+ ):
+ raise InvalidAuthentication('i-names are not supported')
+
+ consumer = Consumer(request.session, OsqaOpenIDStore())
+
+ try:
+ auth_request = consumer.begin(user_url)
+ except DiscoveryFailure:
+ raise InvalidAuthentication(_('Sorry, but your input is not a valid OpenId'))
+
+ #sreg = getattr(settings, 'OPENID_SREG', False)
+
+ #if sreg:
+ # s = SRegRequest()
+ # for sarg in sreg:
+ # if sarg.lower().lstrip() == "policy_url":
+ # s.policy_url = sreg[sarg]
+ # else:
+ # for v in sreg[sarg].split(','):
+ # s.requestField(field_name=v.lower().lstrip(), required=(sarg.lower().lstrip() == "required"))
+ # auth_request.addExtension(s)
+
+ #auth_request.addExtension(SRegRequest(required=['email']))
+
+ if request.session.get('force_email_request', True):
+ axr = AXFetchRequest()
+ axr.add(AttrInfo("http://axschema.org/contact/email", 1, True, "email"))
+ auth_request.addExtension(axr)
+
+ trust_root = getattr(
+ settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/'
+ )
+
+
+ return auth_request.redirectURL(trust_root, redirect_to)
+
+ def process_authentication_request(self, request):
+ consumer = Consumer(request.session, OsqaOpenIDStore())
+
+ query_dict = dict([
+ (k.encode('utf8'), v.encode('utf8')) for k, v in request.GET.items()
+ ])
+
+ #for i in query_dict.items():
+ # print "%s : %s" % i
+
+ url = get_url_host(request) + request.path
+ openid_response = consumer.complete(query_dict, url)
+
+ if openid_response.status == SUCCESS:
+ if request.session.get('force_email_request', True):
+ try:
+ ax = AXFetchResponse.fromSuccessResponse(openid_response)
+ email = ax.getExtensionArgs()['value.ext0.1']
+ request.session['auth_email_request'] = email
+ except Exception, e:
+ pass
+
+ return request.GET['openid.identity']
+ elif openid_response.status == CANCEL:
+ raise InvalidAuthentication(_('The OpenId authentication request was canceled'))
+ elif openid_response.status == FAILURE:
+ raise InvalidAuthentication(_('The OpenId authentication failed: ') + openid_response.message)
+ elif openid_response.status == SETUP_NEEDED:
+ raise InvalidAuthentication(_('Setup needed'))
+ else:
+ raise InvalidAuthentication(_('The OpenId authentication failed with an unknown status: ') + openid_response.status)
+
+ def get_user_data(self, key):
+ return {}
+
+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() \ No newline at end of file
diff --git a/forum_modules/openidauth/models.py b/forum_modules/openidauth/models.py
new file mode 100755
index 00000000..d6cc991e
--- /dev/null
+++ b/forum_modules/openidauth/models.py
@@ -0,0 +1,26 @@
+from django.db import models
+
+class OpenIdNonce(models.Model):
+ server_url = models.URLField()
+ timestamp = models.IntegerField()
+ salt = models.CharField( max_length=50 )
+
+ def __unicode__(self):
+ return "Nonce: %s" % self.nonce
+
+ class Meta:
+ app_label = 'forum'
+
+class OpenIdAssociation(models.Model):
+ server_url = models.TextField(max_length=2047)
+ handle = models.CharField(max_length=255)
+ secret = models.TextField(max_length=255) # Stored base64 encoded
+ issued = models.IntegerField()
+ lifetime = models.IntegerField()
+ assoc_type = models.TextField(max_length=64)
+
+ def __unicode__(self):
+ return "Association: %s, %s" % (self.server_url, self.handle)
+
+ class Meta:
+ app_label = 'forum'
diff --git a/forum_modules/openidauth/settings.py b/forum_modules/openidauth/settings.py
new file mode 100755
index 00000000..3b1c2eec
--- /dev/null
+++ b/forum_modules/openidauth/settings.py
@@ -0,0 +1,9 @@
+OPENID_SREG = {
+ "required": "nickname, email",
+ "optional": "postcode, country",
+ "policy_url": ""
+}
+OPENID_AX = [
+ {"type_uri": "http://axschema.org/contact/email", "count": 1, "required": True, "alias": "email"},
+ {"type_uri": "fullname", "count":1 , "required": False, "alias": "fullname"}
+ ] \ No newline at end of file
diff --git a/forum_modules/openidauth/store.py b/forum_modules/openidauth/store.py
new file mode 100755
index 00000000..93d38a5b
--- /dev/null
+++ b/forum_modules/openidauth/store.py
@@ -0,0 +1,79 @@
+import time, base64, md5
+
+from openid.store import nonce as oid_nonce
+from openid.store.interface import OpenIDStore
+from openid.association import Association as OIDAssociation
+from django.conf import settings
+
+from models import OpenIdNonce as Nonce, OpenIdAssociation as Association
+
+class OsqaOpenIDStore(OpenIDStore):
+ def __init__(self):
+ self.max_nonce_age = 6 * 60 * 60 # Six hours
+
+ def storeAssociation(self, server_url, association):
+ assoc = Association(
+ server_url = server_url,
+ handle = association.handle,
+ secret = base64.encodestring(association.secret),
+ issued = association.issued,
+ lifetime = association.issued,
+ assoc_type = association.assoc_type
+ )
+ assoc.save()
+
+ def getAssociation(self, server_url, handle=None):
+ assocs = []
+ if handle is not None:
+ assocs = Association.objects.filter(
+ server_url = server_url, handle = handle
+ )
+ else:
+ assocs = Association.objects.filter(
+ server_url = server_url
+ )
+ if not assocs:
+ return None
+ associations = []
+ for assoc in assocs:
+ association = OIDAssociation(
+ assoc.handle, base64.decodestring(assoc.secret), assoc.issued,
+ assoc.lifetime, assoc.assoc_type
+ )
+ if association.getExpiresIn() == 0:
+ self.removeAssociation(server_url, assoc.handle)
+ else:
+ associations.append((association.issued, association))
+ if not associations:
+ return None
+ return associations[-1][1]
+
+ def removeAssociation(self, server_url, handle):
+ assocs = list(Association.objects.filter(
+ server_url = server_url, handle = handle
+ ))
+ assocs_exist = len(assocs) > 0
+ for assoc in assocs:
+ assoc.delete()
+ return assocs_exist
+
+ def storeNonce(self, nonce):
+ nonce, created = Nonce.objects.get_or_create(
+ nonce = nonce, defaults={'expires': int(time.time())}
+ )
+
+ def useNonce(self, server_url, timestamp, salt):
+ if abs(timestamp - time.time()) > oid_nonce.SKEW:
+ return False
+
+ try:
+ nonce = Nonce( server_url=server_url, timestamp=timestamp, salt=salt)
+ nonce.save()
+ except:
+ raise
+ else:
+ return 1
+
+ def getAuthKey(self):
+ # Use first AUTH_KEY_LEN characters of md5 hash of SECRET_KEY
+ return md5.new(settings.SECRET_KEY).hexdigest()[:self.AUTH_KEY_LEN]
diff --git a/forum_modules/openidauth/templates/openidurl.html b/forum_modules/openidauth/templates/openidurl.html
new file mode 100755
index 00000000..cd4e77dc
--- /dev/null
+++ b/forum_modules/openidauth/templates/openidurl.html
@@ -0,0 +1,20 @@
+{% load i18n %}
+{% load extra_tags %}
+
+<fieldset>
+ <table>
+ <tr>
+ <td><p id="provider_name_slot">{% trans 'Enter your OpenId Url' %}</p></td>
+ </tr>
+ <tr>
+ <td>
+ <input id="openid_identifier" class="icon_input" name="openid_identifier" type="text"
+ style="width: 500px; background: url('{% media provider.icon %}') no-repeat left center" />
+ </td>
+ <td>
+ <input type="submit" name="ssignin" value="Login" />
+ </td>
+ </tr>
+ </table>
+</fieldset>
+
diff --git a/forum_modules/pgfulltext/DISABLED b/forum_modules/pgfulltext/DISABLED
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/forum_modules/pgfulltext/DISABLED