summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--PENDING5
-rw-r--r--ROADMAP.rst32
-rw-r--r--django_authopenid/__init__.py40
-rw-r--r--django_authopenid/admin.py9
-rw-r--r--django_authopenid/forms.py321
-rw-r--r--django_authopenid/middleware.py28
-rw-r--r--django_authopenid/mimeparse.py160
-rw-r--r--django_authopenid/models.py80
-rwxr-xr-xdjango_authopenid/urls.py62
-rw-r--r--django_authopenid/util.py135
-rwxr-xr-xdjango_authopenid/views.py1169
-rwxr-xr-xfbconnect/fb.py96
-rwxr-xr-xfbconnect/forms.py8
-rwxr-xr-xfbconnect/models.py6
-rwxr-xr-xfbconnect/pjson.py313
-rwxr-xr-xfbconnect/tests.py23
-rwxr-xr-xfbconnect/urls.py19
-rwxr-xr-xfbconnect/views.py112
-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.py2
-rwxr-xr-xforum/models/question.py2
-rwxr-xr-xforum/models/repute.py2
-rwxr-xr-xforum/models/user.py7
-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
-rw-r--r--forum/views/readers.py4
-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__.py (renamed from fbconnect/__init__.py)0
-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
-rw-r--r--locale/en/LC_MESSAGES/django.po1066
-rw-r--r--[-rwxr-xr-x]settings.py5
275 files changed, 2914 insertions, 3169 deletions
diff --git a/PENDING b/PENDING
index 9d2f00ee..2931303c 100644
--- a/PENDING
+++ b/PENDING
@@ -4,10 +4,7 @@ new features (go to law school, get a job, do something real)
Just a joke - pick yourself a task and work on it.
==Refactoring==
-* analyze and split models.py --> models/
-* create forum/modules directory
-* make modules load into the forum app like they
- are the integral part of the forum
+* validate HTML
* set up loading of default settings from inside the /forum dir
* automatic dependency checking for modules
* propose how to rename directory forum --> osqa
diff --git a/ROADMAP.rst b/ROADMAP.rst
new file mode 100644
index 00000000..42f2e8c6
--- /dev/null
+++ b/ROADMAP.rst
@@ -0,0 +1,32 @@
+This document is a map for our activities down the road - therefore ROADMAP.
+ROADMAP does not specify deadlines - those belong to the PENDING file
+
+Intro
+=========
+ROADMAP aims to streamline activities of the OSQA open source project and
+to minimize ad-hoc approaches of "big-picture" level.
+
+With one exception: under extreme time pressure improvised approaches are perfectly acceptable.
+
+Items in this document must be discussed in public via dev@osqa.net
+
+Architecture
+=============
+
+Sub-systems
+-----------------
+* authentication system
+* Q&A system
+
+Authentication system
+-------------------------
+* MUST authenticate people visiting the website via web browsers.
+* Upon successful authentication must associates the visitor with
+ his/her Django system user account
+* MUST allow multiple methods of authentication to the same account
+* MUST support a method to recover lost authentication link by email
+* MAY offer an option to "soft-validate" user's email (send a link
+ with a special key, so that user clicks and we know that email is valid)
+ "soft" - meaning that lack of validation won't block people
+ from using the site
+
diff --git a/django_authopenid/__init__.py b/django_authopenid/__init__.py
deleted file mode 100644
index ff040ed7..00000000
--- a/django_authopenid/__init__.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2007, 2008, Benoît Chesneau
-#
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# * notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# * notice, this list of conditions and the following disclaimer in the
-# * documentation and/or other materials provided with the
-# * distribution. Neither the name of the <ORGANIZATION> nor the names
-# * of its contributors may be used to endorse or promote products
-# * derived from this software without specific prior written
-# * permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""
-Django authentification application to *with openid using django auth contrib/.
-
-This application allow a user to connect to you website with :
- * legacy account : username/password
- * openid url
-"""
-
-__version__ = "0.9.4"
diff --git a/django_authopenid/admin.py b/django_authopenid/admin.py
deleted file mode 100644
index f64ee579..00000000
--- a/django_authopenid/admin.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from django.contrib import admin
-from django_authopenid.models import UserAssociation
-
-
-class UserAssociationAdmin(admin.ModelAdmin):
- """User association admin class"""
-admin.site.register(UserAssociation, UserAssociationAdmin) \ No newline at end of file
diff --git a/django_authopenid/forms.py b/django_authopenid/forms.py
deleted file mode 100644
index 2f34986c..00000000
--- a/django_authopenid/forms.py
+++ /dev/null
@@ -1,321 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2007, 2008, Benoît Chesneau
-#
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# * notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# * notice, this list of conditions and the following disclaimer in the
-# * documentation and/or other materials provided with the
-# * distribution. Neither the name of the <ORGANIZATION> nor the names
-# * of its contributors may be used to endorse or promote products
-# * derived from this software without specific prior written
-# * permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-from django import forms
-from django.contrib.auth.models import User
-from django.contrib.auth import authenticate
-from django.utils.translation import ugettext as _
-from django.conf import settings
-import types
-import re
-from django.utils.safestring import mark_safe
-from recaptcha_django import ReCaptchaField
-from forum.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
-EXTERNAL_LOGIN_APP = settings.LOAD_EXTERNAL_LOGIN_APP()
-
-# needed for some linux distributions like debian
-try:
- from openid.yadis import xri
-except ImportError:
- from yadis import xri
-
-from forum.utils.forms import clean_next
-from django_authopenid.models import ExternalLoginData
-
-__all__ = ['OpenidSigninForm', 'ClassicLoginForm', 'OpenidVerifyForm',
- 'OpenidRegisterForm', 'ClassicRegisterForm', 'ChangePasswordForm',
- 'ChangeEmailForm', 'EmailPasswordForm', 'DeleteForm',
- 'ChangeOpenidForm']
-
-class OpenidSigninForm(forms.Form):
- """ signin form """
- openid_url = forms.CharField(max_length=255, widget=forms.widgets.TextInput(attrs={'class': 'openid-login-input', 'size':80}))
- next = NextUrlField()
-
- def clean_openid_url(self):
- """ test if openid is accepted """
- if 'openid_url' in self.cleaned_data:
- openid_url = self.cleaned_data['openid_url']
- if xri.identifierScheme(openid_url) == 'XRI' and getattr(
- settings, 'OPENID_DISALLOW_INAMES', False
- ):
- raise forms.ValidationError(_('i-names are not supported'))
- return self.cleaned_data['openid_url']
-
-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):
- """
- this clean function actually cleans username and password
-
- test if password is valid for this username
- this is really the "authenticate" function
- also openid_auth is not an authentication backend
- since it's written in a way that does not comply with
- the Django convention
- """
-
- error_list = []
- username = self.cleaned_data['username']
- password = self.cleaned_data['password']
-
- self.user_cache = None
- if username and password:
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- pw_ok = False
- try:
- pw_ok = EXTERNAL_LOGIN_APP.api.check_password(username,password)
- except forms.ValidationError, e:
- error_list.extend(e.messages)
- if pw_ok:
- external_user = ExternalLoginData.objects.get(external_username=username)
- if external_user.user == None:
- return self.cleaned_data
- user = external_user.user
- openid_logins = user.userassociation_set.all()
-
- if len(openid_logins) > 0:
- msg1 = _('Account with this name already exists on the forum')
- msg2 = _('can\'t have two logins to the same account yet, sorry.')
- error_list.append(msg1)
- error_list.append(msg2)
- self._errors['__all__'] = forms.util.ErrorList(error_list)
- return self.cleaned_data
- else:
- #synchronize password with external login
- user.set_password(password)
- user.save()
- #this auth will always succeed
- self.user_cache = authenticate(username=user.username,\
- password=password)
- else:
- #keep self.user_cache == None
- #nothing to do, error message will be set below
- pass
- else:
- 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
-
-
-class OpenidRegisterForm(forms.Form):
- """ openid signin form """
- next = NextUrlField()
- username = UserNameField()
- email = UserEmailField()
-
-class OpenidVerifyForm(forms.Form):
- """ openid verify form (associate an openid with an account) """
- next = NextUrlField()
- username = UserNameField(must_exist=True)
- password = forms.CharField(max_length=128,
- widget=forms.widgets.PasswordInput(attrs={'class':'required login'}))
-
- def __init__(self, data=None, files=None, auto_id='id_%s',
- prefix=None, initial=None):
- super(OpenidVerifyForm, self).__init__(data, files, auto_id,
- prefix, initial)
- self.user_cache = None
-
- def clean_password(self):
- """ test if password is valid for this user """
- if 'username' in self.cleaned_data and \
- 'password' in self.cleaned_data:
- self.user_cache = authenticate(
- username = self.cleaned_data['username'],
- password = self.cleaned_data['password']
- )
- if self.user_cache is None:
- raise forms.ValidationError(_("Please enter a valid \
- username and password. Note that both fields are \
- case-sensitive."))
- elif self.user_cache.is_active == False:
- raise forms.ValidationError(_("This account is inactive."))
- return self.cleaned_data['password']
-
- def get_user(self):
- """ get authenticated user """
- return self.user_cache
-
-class ClassicRegisterForm(SetPasswordForm):
- """ legacy registration form """
-
- next = NextUrlField()
- username = UserNameField()
- email = UserEmailField()
- #fields password1 and password2 are inherited
- recaptcha = ReCaptchaField()
-
-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 ChangeEmailForm(forms.Form):
- """ change email form """
- email = UserEmailField(skip_clean=True)
-
- def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, \
- initial=None, user=None):
- super(ChangeEmailForm, self).__init__(data, files, auto_id,
- prefix, initial)
- self.user = user
-
- def clean_email(self):
- """ check if email don't exist """
- if 'email' in self.cleaned_data:
- if settings.EMAIL_UNIQUE == True:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- if self.user and self.user == user:
- return self.cleaned_data['email']
- except User.DoesNotExist:
- return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
- raise forms.ValidationError(u'There is already more than one \
- account registered with that e-mail address. Please try \
- another.')
- raise forms.ValidationError(u'This email is already registered \
- in our database. Please choose another.')
- else:
- return self.cleaned_data['email']
-
-class ChangeopenidForm(forms.Form):
- """ change openid form """
- openid_url = forms.CharField(max_length=255,
- widget=forms.TextInput(attrs={'class': "required" }))
-
- def __init__(self, data=None, user=None, *args, **kwargs):
- if user is None:
- raise TypeError("Keyword argument 'user' must be supplied")
- super(ChangeopenidForm, self).__init__(data, *args, **kwargs)
- self.user = user
-
-class DeleteForm(forms.Form):
- """ confirm form to delete an account """
- confirm = forms.CharField(widget=forms.CheckboxInput(attrs={'class':'required'}))
- password = forms.CharField(widget=forms.PasswordInput(attrs={'class':'required'}))
-
- def __init__(self, data=None, files=None, auto_id='id_%s',
- prefix=None, initial=None, user=None):
- super(DeleteForm, self).__init__(data, files, auto_id, prefix, initial)
- self.test_openid = False
- self.user = user
-
- def clean_password(self):
- """ check if we have to test a legacy account or not """
- if 'password' in self.cleaned_data:
- if not self.user.check_password(self.cleaned_data['password']):
- self.test_openid = True
- return self.cleaned_data['password']
-
-
-class EmailPasswordForm(forms.Form):
- """ send new password form """
- username = UserNameField(skip_clean=True,label=mark_safe(_('Your user name (<i>required</i>)')))
-
- def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
- initial=None):
- super(EmailPasswordForm, self).__init__(data, files, auto_id,
- prefix, initial)
- self.user_cache = None
-
- def clean_username(self):
- """ get user for this username """
- if 'username' in self.cleaned_data:
- try:
- self.user_cache = User.objects.get(
- username = self.cleaned_data['username'])
- except:
- raise forms.ValidationError(_("Incorrect username."))
- return self.cleaned_data['username']
diff --git a/django_authopenid/middleware.py b/django_authopenid/middleware.py
deleted file mode 100644
index 2be8da90..00000000
--- a/django_authopenid/middleware.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-from django_authopenid import mimeparse
-from django.http import HttpResponseRedirect
-from django.core.urlresolvers import reverse
-from django.conf import settings
-import logging
-
-__all__ = ["OpenIDMiddleware"]
-
-class OpenIDMiddleware(object):
- """
- Populate request.openid. This comes either from cookie or from
- session, depending on the presence of OPENID_USE_SESSIONS.
- """
- def process_request(self, request):
- request.openid = request.session.get('openid', None)
- logging.debug('openid in session is: %s' % str(request.openid))
-
- def process_response(self, request, response):
- if response.status_code != 200 or len(response.content) < 200:
- return response
- path = request.get_full_path()
- if path == "/" and request.META.has_key('HTTP_ACCEPT') and \
- mimeparse.best_match(['text/html', 'application/xrds+xml'],
- request.META['HTTP_ACCEPT']) == 'application/xrds+xml':
- logging.debug('redirecting to yadis_xrdf:%s' % reverse('yadis_xrdf'))
- return HttpResponseRedirect(reverse('yadis_xrdf'))
- return response
diff --git a/django_authopenid/mimeparse.py b/django_authopenid/mimeparse.py
deleted file mode 100644
index ab02eab0..00000000
--- a/django_authopenid/mimeparse.py
+++ /dev/null
@@ -1,160 +0,0 @@
-"""MIME-Type Parser
-
-This module provides basic functions for handling mime-types. It can handle
-matching mime-types against a list of media-ranges. See section 14.1 of
-the HTTP specification [RFC 2616] for a complete explaination.
-
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
-
-Contents:
- - parse_mime_type(): Parses a mime-type into it's component parts.
- - parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q' quality parameter.
- - quality(): Determines the quality ('q') of a mime-type when compared against a list of media-ranges.
- - quality_parsed(): Just like quality() except the second parameter must be pre-parsed.
- - best_match(): Choose the mime-type with the highest quality ('q') from a list of candidates.
-"""
-
-__version__ = "0.1.1"
-__author__ = 'Joe Gregorio'
-__email__ = "joe@bitworking.org"
-__credits__ = ""
-
-def parse_mime_type(mime_type):
- """Carves up a mime_type and returns a tuple of the
- (type, subtype, params) where 'params' is a dictionary
- of all the parameters for the media range.
- For example, the media range 'application/xhtml;q=0.5' would
- get parsed into:
-
- ('application', 'xhtml', {'q', '0.5'})
- """
- parts = mime_type.split(";")
- params = dict([tuple([s.strip() for s in param.split("=")])\
- for param in parts[1:] ])
- (type, subtype) = parts[0].split("/")
- return (type.strip(), subtype.strip(), params)
-
-def parse_media_range(range):
- """Carves up a media range and returns a tuple of the
- (type, subtype, params) where 'params' is a dictionary
- of all the parameters for the media range.
- For example, the media range 'application/*;q=0.5' would
- get parsed into:
-
- ('application', '*', {'q', '0.5'})
-
- In addition this function also guarantees that there
- is a value for 'q' in the params dictionary, filling it
- in with a proper default if necessary.
- """
- (type, subtype, params) = parse_mime_type(range)
- if not params.has_key('q') or not params['q'] or \
- not float(params['q']) or float(params['q']) > 1\
- or float(params['q']) < 0:
- params['q'] = '1'
- return (type, subtype, params)
-
-def quality_parsed(mime_type, parsed_ranges):
- """Find the best match for a given mime_type against
- a list of media_ranges that have already been
- parsed by parse_media_range(). Returns the
- 'q' quality parameter of the best match, 0 if no
- match was found. This function bahaves the same as quality()
- except that 'parsed_ranges' must be a list of
- parsed media ranges. """
- best_fitness = -1
- best_match = ""
- best_fit_q = 0
- (target_type, target_subtype, target_params) =\
- parse_media_range(mime_type)
- for (type, subtype, params) in parsed_ranges:
- param_matches = reduce(lambda x, y: x+y, [1 for (key, value) in \
- target_params.iteritems() if key != 'q' and \
- params.has_key(key) and value == params[key]], 0)
- if (type == target_type or type == '*' or target_type == '*') and \
- (subtype == target_subtype or subtype == '*' or target_subtype == '*'):
- fitness = (type == target_type) and 100 or 0
- fitness += (subtype == target_subtype) and 10 or 0
- fitness += param_matches
- if fitness > best_fitness:
- best_fitness = fitness
- best_fit_q = params['q']
-
- return float(best_fit_q)
-
-def quality(mime_type, ranges):
- """Returns the quality 'q' of a mime_type when compared
- against the media-ranges in ranges. For example:
-
- >>> quality('text/html','text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
- 0.7
-
- """
- parsed_ranges = [parse_media_range(r) for r in ranges.split(",")]
- return quality_parsed(mime_type, parsed_ranges)
-
-def best_match(supported, header):
- """Takes a list of supported mime-types and finds the best
- match for all the media-ranges listed in header. The value of
- header must be a string that conforms to the format of the
- HTTP Accept: header. The value of 'supported' is a list of
- mime-types.
-
- >>> best_match(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1')
- 'text/xml'
- """
- parsed_header = [parse_media_range(r) for r in header.split(",")]
- weighted_matches = [(quality_parsed(mime_type, parsed_header), mime_type)\
- for mime_type in supported]
- weighted_matches.sort()
- return weighted_matches[-1][0] and weighted_matches[-1][1] or ''
-
-if __name__ == "__main__":
- import unittest
-
- class TestMimeParsing(unittest.TestCase):
-
- def test_parse_media_range(self):
- self.assert_(('application', 'xml', {'q': '1'}) == parse_media_range('application/xml;q=1'))
- self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml'))
- self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml;q='))
- self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml ; q='))
- self.assertEqual(('application', 'xml', {'q': '1', 'b': 'other'}), parse_media_range('application/xml ; q=1;b=other'))
- self.assertEqual(('application', 'xml', {'q': '1', 'b': 'other'}), parse_media_range('application/xml ; q=2;b=other'))
-
- def test_rfc_2616_example(self):
- accept = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5"
- self.assertEqual(1, quality("text/html;level=1", accept))
- self.assertEqual(0.7, quality("text/html", accept))
- self.assertEqual(0.3, quality("text/plain", accept))
- self.assertEqual(0.5, quality("image/jpeg", accept))
- self.assertEqual(0.4, quality("text/html;level=2", accept))
- self.assertEqual(0.7, quality("text/html;level=3", accept))
-
- def test_best_match(self):
- mime_types_supported = ['application/xbel+xml', 'application/xml']
- # direct match
- self.assertEqual(best_match(mime_types_supported, 'application/xbel+xml'), 'application/xbel+xml')
- # direct match with a q parameter
- self.assertEqual(best_match(mime_types_supported, 'application/xbel+xml; q=1'), 'application/xbel+xml')
- # direct match of our second choice with a q parameter
- self.assertEqual(best_match(mime_types_supported, 'application/xml; q=1'), 'application/xml')
- # match using a subtype wildcard
- self.assertEqual(best_match(mime_types_supported, 'application/*; q=1'), 'application/xml')
- # match using a type wildcard
- self.assertEqual(best_match(mime_types_supported, '*/*'), 'application/xml')
-
- mime_types_supported = ['application/xbel+xml', 'text/xml']
- # match using a type versus a lower weighted subtype
- self.assertEqual(best_match(mime_types_supported, 'text/*;q=0.5,*/*; q=0.1'), 'text/xml')
- # fail to match anything
- self.assertEqual(best_match(mime_types_supported, 'text/html,application/atom+xml; q=0.9'), '')
-
- def test_support_wildcards(self):
- mime_types_supported = ['image/*', 'application/xml']
- # match using a type wildcard
- self.assertEqual(best_match(mime_types_supported, 'image/png'), 'image/*')
- # match using a wildcard for both requested and supported
- self.assertEqual(best_match(mime_types_supported, 'image/*'), 'image/*')
-
- unittest.main() \ No newline at end of file
diff --git a/django_authopenid/models.py b/django_authopenid/models.py
deleted file mode 100644
index a12c2fec..00000000
--- a/django_authopenid/models.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- coding: utf-8 -*-
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.db import models
-
-import hashlib, random, sys, os, time
-
-__all__ = ['Nonce', 'Association', 'UserAssociation',
- 'UserPasswordQueueManager', 'UserPasswordQueue']
-
-class Nonce(models.Model):
- """ openid nonce """
- server_url = models.CharField(max_length=255)
- timestamp = models.IntegerField()
- salt = models.CharField(max_length=40)
-
- def __unicode__(self):
- return u"Nonce: %s" % self.id
-
-
-class Association(models.Model):
- """ association openid url and lifetime """
- 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 u"Association: %s, %s" % (self.server_url, self.handle)
-
-class UserAssociation(models.Model):
- """
- model to manage association between openid and user
- """
- openid_url = models.CharField(blank=False, max_length=255)
- user = models.ForeignKey(User, unique=True)
-
- def __unicode__(self):
- return "Openid %s with user %s" % (self.openid_url, self.user)
-
-class UserPasswordQueueManager(models.Manager):
- """ manager for UserPasswordQueue object """
- def get_new_confirm_key(self):
- "Returns key that isn't being used."
- # The random module is seeded when this Apache child is created.
- # Use SECRET_KEY as added salt.
- while 1:
- confirm_key = hashlib.md5("%s%s%s%s" % (
- random.randint(0, sys.maxint - 1), os.getpid(),
- time.time(), settings.SECRET_KEY)).hexdigest()
- try:
- self.get(confirm_key=confirm_key)
- except self.model.DoesNotExist:
- break
- return confirm_key
-
-
-class UserPasswordQueue(models.Model):
- """
- model for new password queue.
- """
- user = models.ForeignKey(User, unique=True)
- new_password = models.CharField(max_length=30)
- confirm_key = models.CharField(max_length=40)
-
- objects = UserPasswordQueueManager()
-
- def __unicode__(self):
- return self.user.username
-
-class ExternalLoginData(models.Model):
- """this class was added by Evgeny to associate
- external authentication user with django user
- probably it just does not belong here... (EF)
- """
- external_username = models.CharField(max_length=40, unique=True, null=False)
- external_session_data = models.TextField()
- user = models.ForeignKey(User, null=True)
diff --git a/django_authopenid/urls.py b/django_authopenid/urls.py
deleted file mode 100755
index e1986d19..00000000
--- a/django_authopenid/urls.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# -*- coding: utf-8 -*-
-from django.conf.urls.defaults import patterns, url
-from django.utils.translation import ugettext as _
-from django.conf import settings
-
-#print 'stuff to import %s' % settings.EXTERNAL_LOGIN_APP.__name__ + '.views'
-#try:
-# settings.EXTERNAL_LOGIN_APP = __import__('mediawiki.views')
-#print 'stuff to import %s' % settings.EXTERNAL_LOGIN_APP.__name__ + '.views'
-#try:
-# print 'imported fine'
-# print settings.EXTERNAL_LOGIN_APP.__dict__.keys()
-#except:
-# print 'dammit!'
-#from mediawiki.views import signup_view
-#settings.EXTERNAL_LOGIN_APP.views.signup_view()
-
-#print settings.EXTERNAL_LOGIN_APP.__dict__.keys()
-urlpatterns = patterns('django_authopenid.views',
- # yadis rdf
- url(r'^yadis.xrdf$', 'xrdf', name='yadis_xrdf'),
- # manage account registration
- url(r'^%s$' % _('signin/'), 'signin', name='user_signin'),
- url(r'^%s%s$' % (_('signin/'),_('newquestion/')), 'signin', kwargs = {'newquestion':True}, name='user_signin_new_question'),
- url(r'^%s%s$' % (_('signin/'),_('newanswer/')), 'signin', kwargs = {'newanswer':True}, name='user_signin_new_answer'),
- url(r'^%s$' % _('signout/'), 'signout', name='user_signout'),
- url(r'^%s%s$' % (_('signin/'), _('complete/')), 'complete_signin',
- name='user_complete_signin'),
- url(r'^%s$' % _('register/'), 'register', name='user_register'),
- url(r'^%s$' % _('signup/'), 'signup', name='user_signup'),
- #disable current sendpw function
- url(r'^%s$' % _('sendpw/'), 'sendpw', name='user_sendpw'),
- url(r'^%s%s$' % (_('password/'), _('confirm/')), 'confirmchangepw', name='user_confirmchangepw'),
-
- # manage account settings
- url(r'^$', _('account_settings'), name='user_account_settings'),
- url(r'^%s$' % _('password/'), 'changepw', name='user_changepw'),
- url(r'^%s%s$' % (_('email/'),_('validate/')), 'changeemail', name='user_validateemail',kwargs = {'action':'validate'}),
- url(r'^%s%s$' % (_('email/'), _('change/')), 'changeemail', name='user_changeemail'),
- url(r'^%s%s$' % (_('email/'), _('sendkey/')), 'send_email_key', name='send_email_key'),
- url(r'^%s%s(?P<id>\d+)/(?P<key>[\dabcdef]{32})/$' % (_('email/'), _('verify/')), 'verifyemail', name='user_verifyemail'),
- url(r'^%s$' % _('openid/'), 'changeopenid', name='user_changeopenid'),
- url(r'^%s$' % _('delete/'), 'delete', name='user_delete'),
-)
-
-#todo move these out of this file completely
-if settings.USE_EXTERNAL_LEGACY_LOGIN:
- from forum.forms import NotARobotForm
- EXTERNAL_LOGIN_APP = settings.LOAD_EXTERNAL_LOGIN_APP()
- urlpatterns += patterns('',
- url('^%s$' % _('external-login/forgot-password/'),\
- 'django_authopenid.views.external_legacy_login_info', \
- name='user_external_legacy_login_issues'),
- url('^%s$' % _('external-login/signup/'), \
- EXTERNAL_LOGIN_APP.views.signup,\
- name='user_external_legacy_login_signup'),
-# url('^%s$' % _('external-login/signup/'), \
-# EXTERNAL_LOGIN_APP.forms.RegisterFormWizard( \
-# [EXTERNAL_LOGIN_APP.forms.RegisterForm, \
-# NotARobotForm]),\
-# name='user_external_legacy_login_signup'),
- )
diff --git a/django_authopenid/util.py b/django_authopenid/util.py
deleted file mode 100644
index cd2c2e2c..00000000
--- a/django_authopenid/util.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# -*- coding: utf-8 -*-
-from openid.store.interface import OpenIDStore
-from openid.association import Association as OIDAssociation
-from openid.extensions import sreg
-import openid.store
-
-from django.db.models.query import Q
-from django.conf import settings
-from django.core.urlresolvers import reverse
-
-# needed for some linux distributions like debian
-try:
- from openid.yadis import xri
-except:
- from yadis import xri
-
-import time, base64, hashlib, operator, logging
-from forum.utils.forms import clean_next, get_next_url
-
-from models import Association, Nonce
-
-__all__ = ['OpenID', 'DjangoOpenIDStore', 'from_openid_response', 'clean_next']
-
-class OpenID:
- def __init__(self, openid_, issued, attrs=None, sreg_=None):
- logging.debug('init janrain openid object')
- self.openid = openid_
- self.issued = issued
- self.attrs = attrs or {}
- self.sreg = sreg_ or {}
- self.is_iname = (xri.identifierScheme(openid_) == 'XRI')
-
- def __repr__(self):
- return '<OpenID: %s>' % self.openid
-
- def __str__(self):
- return self.openid
-
-class DjangoOpenIDStore(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 useNonce(self, server_url, timestamp, salt):
- if abs(timestamp - time.time()) > openid.store.nonce.SKEW:
- return False
-
- query = [
- Q(server_url__exact=server_url),
- Q(timestamp__exact=timestamp),
- Q(salt__exact=salt),
- ]
- try:
- ononce = Nonce.objects.get(reduce(operator.and_, query))
- except Nonce.DoesNotExist:
- ononce = Nonce(
- server_url=server_url,
- timestamp=timestamp,
- salt=salt
- )
- ononce.save()
- return True
-
- ononce.delete()
-
- return False
-
- def cleanupNonce(self):
- Nonce.objects.filter(timestamp<int(time.time()) - nonce.SKEW).delete()
-
- def cleanupAssociations(self):
- Association.objects.extra(where=['issued + lifetimeint<(%s)' % time.time()]).delete()
-
- def getAuthKey(self):
- # Use first AUTH_KEY_LEN characters of md5 hash of SECRET_KEY
- return hashlib.md5(settings.SECRET_KEY).hexdigest()[:self.AUTH_KEY_LEN]
-
- def isDumb(self):
- return False
-
-def from_openid_response(openid_response):
- """ return openid object from response """
- issued = int(time.time())
- sreg_resp = sreg.SRegResponse.fromSuccessResponse(openid_response) \
- or []
-
- return OpenID(
- openid_response.identity_url, issued, openid_response.signed_fields,
- dict(sreg_resp)
- )
diff --git a/django_authopenid/views.py b/django_authopenid/views.py
deleted file mode 100755
index 7c7d9e07..00000000
--- a/django_authopenid/views.py
+++ /dev/null
@@ -1,1169 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2007, 2008, Benoît Chesneau
-# Copyright (c) 2007 Simon Willison, original work on django-openid
-#
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# * notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright
-# * notice, this list of conditions and the following disclaimer in the
-# * documentation and/or other materials provided with the
-# * distribution. Neither the name of the <ORGANIZATION> nor the names
-# * of its contributors may be used to endorse or promote products
-# * derived from this software without specific prior written
-# * permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
-# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from django.http import HttpResponseRedirect, get_host, Http404, \
- HttpResponseServerError
-from django.shortcuts import render_to_response
-from django.template import RequestContext, loader, Context
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth import authenticate
-from django.core.urlresolvers import reverse
-from django.utils.encoding import smart_unicode
-from django.utils.html import escape
-from django.utils.translation import ugettext as _
-from django.utils.http import urlquote_plus
-from django.utils.safestring import mark_safe
-from django.core.mail import send_mail
-from django.views.defaults import server_error
-
-from openid.consumer.consumer import Consumer, \
- SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
-from openid.consumer.discover import DiscoveryFailure
-from openid.extensions import sreg
-# needed for some linux distributions like debian
-try:
- from openid.yadis import xri
-except ImportError:
- from yadis import xri
-
-import re
-import urllib
-
-from forum.forms import SimpleEmailSubscribeForm
-from django_authopenid.util import OpenID, DjangoOpenIDStore, from_openid_response
-from django_authopenid.models import UserAssociation, UserPasswordQueue, ExternalLoginData
-from django_authopenid.forms import OpenidSigninForm, ClassicLoginForm, OpenidRegisterForm, \
- OpenidVerifyForm, ClassicRegisterForm, ChangePasswordForm, ChangeEmailForm, \
- ChangeopenidForm, DeleteForm, EmailPasswordForm
-import logging
-from forum.utils.forms import get_next_url
-
-EXTERNAL_LOGIN_APP = settings.LOAD_EXTERNAL_LOGIN_APP()
-
-def login(request,user):
- from django.contrib.auth import login as _login
- from forum.models import user_logged_in #custom signal
-
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- EXTERNAL_LOGIN_APP.api.login(request,user)
-
- #1) get old session key
- session_key = request.session.session_key
- #2) login and get new session key
- _login(request,user)
- #3) send signal with old session key as argument
- logging.debug('logged in user %s with session key %s' % (user.username, session_key))
- user_logged_in.send(user=user,session_key=session_key,sender=None)
-
-def logout(request):
- from django.contrib.auth import logout as _logout#for login I've added wrapper below - called login
- _logout(request)
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- EXTERNAL_LOGIN_APP.api.logout(request)
-
-def get_url_host(request):
- if request.is_secure():
- protocol = 'https'
- else:
- protocol = 'http'
- host = escape(get_host(request))
- return '%s://%s' % (protocol, host)
-
-def get_full_url(request):
- return get_url_host(request) + request.get_full_path()
-
-def ask_openid(request, openid_url, redirect_to, on_failure=None,
- sreg_request=None):
- """ basic function to ask openid and return response """
- request.encoding = 'UTF-8'
- on_failure = on_failure or signin_failure
-
- trust_root = getattr(
- settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/'
- )
- if xri.identifierScheme(openid_url) == 'XRI' and getattr(
- settings, 'OPENID_DISALLOW_INAMES', False
- ):
- msg = _("i-names are not supported")
- logging.debug('openid failed becaise i-names are not supported')
- return on_failure(request, msg)
- consumer = Consumer(request.session, DjangoOpenIDStore())
- try:
- auth_request = consumer.begin(openid_url)
- except DiscoveryFailure:
- msg = _(u"OpenID %(openid_url)s is invalid" % {'openid_url':openid_url})
- logging.debug(msg)
- return on_failure(request, msg)
-
- logging.debug('openid seemed to work')
- if sreg_request:
- logging.debug('adding sreg_request - wtf it is?')
- auth_request.addExtension(sreg_request)
- redirect_url = auth_request.redirectURL(trust_root, redirect_to)
- logging.debug('redirecting to %s' % redirect_url)
- return HttpResponseRedirect(redirect_url)
-
-def complete(request, on_success=None, on_failure=None, return_to=None):
- """ complete openid signin """
- on_success = on_success or default_on_success
- on_failure = on_failure or default_on_failure
-
- logging.debug('in django_authopenid.complete')
-
- consumer = Consumer(request.session, DjangoOpenIDStore())
- # make sure params are encoded in utf8
- params = dict((k,smart_unicode(v)) for k, v in request.GET.items())
- openid_response = consumer.complete(params, return_to)
-
- if openid_response.status == SUCCESS:
- logging.debug('SUCCESS')
- return on_success(request, openid_response.identity_url,
- openid_response)
- elif openid_response.status == CANCEL:
- logging.debug('CANCEL')
- return on_failure(request, 'The request was canceled')
- elif openid_response.status == FAILURE:
- logging.debug('FAILURE')
- return on_failure(request, openid_response.message)
- elif openid_response.status == SETUP_NEEDED:
- logging.debug('SETUP NEEDED')
- return on_failure(request, 'Setup needed')
- else:
- logging.debug('BAD OPENID STATUS')
- assert False, "Bad openid status: %s" % openid_response.status
-
-def default_on_success(request, identity_url, openid_response):
- """ default action on openid signin success """
- logging.debug('')
- request.session['openid'] = from_openid_response(openid_response)
- logging.debug('performing default action on openid success %s' % get_next_url(request))
- return HttpResponseRedirect(get_next_url(request))
-
-def default_on_failure(request, message):
- """ default failure action on signin """
- logging.debug('default openid failure action')
- return render_to_response('openid_failure.html', {
- 'message': message
- })
-
-
-def not_authenticated(func):
- """ decorator that redirect user to next page if
- he is already logged."""
- def decorated(request, *args, **kwargs):
- if request.user.is_authenticated():
- return HttpResponseRedirect(get_next_url(request))
- return func(request, *args, **kwargs)
- return decorated
-
-@not_authenticated
-def signin(request,newquestion=False,newanswer=False):
- """
- signin page. It manages the legacy authentification (user/password)
- and openid authentification
-
- url: /signin/
-
- template : authopenid/signin.htm
- """
- logging.debug('in signin view')
- request.encoding = 'UTF-8'
- on_failure = signin_failure
- email_feeds_form = SimpleEmailSubscribeForm()
- next = get_next_url(request)
- form_signin = OpenidSigninForm(initial={'next':next})
- form_auth = ClassicLoginForm(initial={'next':next})
-
- if request.method == 'POST':
- #'blogin' - password login
- if 'blogin' in request.POST.keys():
- logging.debug('processing classic login form submission')
- form_auth = ClassicLoginForm(request.POST)
- if form_auth.is_valid():
- #have login and password and need to login through external website
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- username = form_auth.cleaned_data['username']
- password = form_auth.cleaned_data['password']
- next = form_auth.cleaned_data['next']
- if form_auth.get_user() == None:
- #need to create internal user
-
- #1) save login and password temporarily in session
- request.session['external_username'] = username
- request.session['external_password'] = password
-
- #2) try to extract user email and nickname from external service
- email = EXTERNAL_LOGIN_APP.api.get_email(username,password)
- screen_name = EXTERNAL_LOGIN_APP.api.get_screen_name(username,password)
-
- #3) see if username clashes with some existing user
- #if so, we have to prompt the user to pick a different name
- username_taken = User.is_username_taken(screen_name)
-
- email_feeds_form = SimpleEmailSubscribeForm()
- form_data = {'username':screen_name,'email':email,'next':next}
- form = OpenidRegisterForm(initial=form_data)
- template_data = {'form1':form,'username':screen_name,\
- 'email_feeds_form':email_feeds_form,\
- 'provider':mark_safe(settings.EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME),\
- 'login_type':'legacy',\
- 'gravatar_faq_url':reverse('faq') + '#gravatar',\
- 'external_login_name_is_taken':username_taken}
- return render_to_response('authopenid/complete.html',template_data,\
- context_instance=RequestContext(request))
- else:
- #user existed, external password is ok
- user = form_auth.get_user()
- login(request,user)
- response = HttpResponseRedirect(get_next_url(request))
- EXTERNAL_LOGIN_APP.api.set_login_cookies(response,user)
- return response
- else:
- #regular password authentication
- user = form_auth.get_user()
- login(request, user)
- return HttpResponseRedirect(get_next_url(request))
-
- elif 'bnewaccount' in request.POST.keys():
- logging.debug('processing classic (login/password) create account form submission')
- #register externally logged in password user with a new local account
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- form = OpenidRegisterForm(request.POST)
- email_feeds_form = SimpleEmailSubscribeForm(request.POST)
- form1_is_valid = form.is_valid()
- form2_is_valid = email_feeds_form.is_valid()
- if form1_is_valid and form2_is_valid:
- #create the user
- username = form.cleaned_data['username']
- password = request.session.get('external_password',None)
- email = form.cleaned_data['email']
- if password and username:
- User.objects.create_user(username,email,password)
- user = authenticate(username=username,password=password)
- EXTERNAL_LOGIN_APP.api.connect_local_user_to_external_user(user,username,password)
- external_username = request.session['external_username']
- eld = ExternalLoginData.objects.get(external_username=external_username)
- eld.user = user
- eld.save()
- login(request,user)
- email_feeds_form.save(user)
- del request.session['external_username']
- del request.session['external_password']
- response = HttpResponseRedirect(reverse('index'))
- EXTERNAL_LOGIN_APP.api.set_login_cookies(response, user)
- return response
- else:
- if password:
- del request.session['external_username']
- if username:
- del request.session['external_password']
- return HttpResponseServerError()
- else:
- username = request.POST.get('username',None)
- provider = mark_safe(settings.EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME)
- username_taken = User.is_username_taken(username)
- data = {'login_type':'legacy','form1':form,'username':username,\
- 'email_feeds_form':email_feeds_form,'provider':provider,\
- 'gravatar_faq_url':reverse('faq') + '#gravatar',\
- 'external_login_name_is_taken':username_taken}
- return render_to_response('authopenid/complete.html',data,
- context_instance=RequestContext(request))
- else:
- raise Http404
-
- elif 'bsignin' in request.POST.keys() or 'openid_username' in request.POST.keys():
- logging.debug('processing signin with openid submission')
- form_signin = OpenidSigninForm(request.POST)
- if form_signin.is_valid():
- logging.debug('OpenidSigninForm is valid')
- next = form_signin.cleaned_data['next']
- sreg_req = sreg.SRegRequest(optional=['nickname', 'email'])
- redirect_to = "%s%s?%s" % (
- get_url_host(request),
- reverse('user_complete_signin'),
- urllib.urlencode({'next':next})
- )
- return ask_openid(request,
- form_signin.cleaned_data['openid_url'],
- redirect_to,
- on_failure=signin_failure,
- sreg_request=sreg_req)
- else:
- logging.debug('OpenidSigninForm is NOT valid! -> redisplay login view')
-
- #if request is GET
- if request.method == 'GET':
- logging.debug('request method was GET')
- question = None
- if newquestion == True:
- from forum.models import AnonymousQuestion as AQ
- session_key = request.session.session_key
- logging.debug('retrieving anonymously posted question associated with session %s' % session_key)
- qlist = AQ.objects.filter(session_key=session_key).order_by('-added_at')
- if len(qlist) > 0:
- question = qlist[0]
- answer = None
- if newanswer == True:
- from forum.models import AnonymousAnswer as AA
- session_key = request.session.session_key
- logging.debug('retrieving posted answer associated with session %s' % session_key)
- alist = AA.objects.filter(session_key=session_key).order_by('-added_at')
- if len(alist) > 0:
- answer = alist[0]
-
- logging.debug('showing signin view')
- return render_to_response('authopenid/signin.html', {
- 'question':question,
- 'answer':answer,
- 'form1': form_auth,
- 'form2': form_signin,
- 'msg': request.GET.get('msg',''),
- 'sendpw_url': reverse('user_sendpw'),
- 'fb_api_key': settings.FB_API_KEY,
- }, context_instance=RequestContext(request))
-
-def complete_signin(request):
- """ in case of complete signin with openid """
- logging.debug('')#blank log just for the trace
- return complete(request, signin_success, signin_failure,
- get_url_host(request) + reverse('user_complete_signin'))
-
-def signin_success(request, identity_url, openid_response):
- """
- openid signin success.
-
- If the openid is already registered, the user is redirected to
- url set par next or in settings with OPENID_REDIRECT_NEXT variable.
- If none of these urls are set user is redirectd to /.
-
- if openid isn't registered user is redirected to register page.
- """
-
- logging.debug('')
- openid_ = from_openid_response(openid_response) #create janrain OpenID object
- request.session['openid'] = openid_
- try:
- logging.debug('trying to get user associated with this openid...')
- rel = UserAssociation.objects.get(openid_url__exact = str(openid_))
- logging.debug('success')
- except:
- logging.debug('failed --> try to register brand new user')
- # try to register this new user
- return register(request)
- user_ = rel.user
- if user_.is_active:
- user_.backend = "django.contrib.auth.backends.ModelBackend"
- logging.debug('user is active --> attached django auth ModelBackend --> calling login')
- login(request, user_)
- logging.debug('success')
- else:
- logging.debug('user is inactive, do not log them in')
- logging.debug('redirecting to %s' % get_next_url(request))
- return HttpResponseRedirect(get_next_url(request))
-
-def is_association_exist(openid_url):
- """ test if an openid is already in database """
- is_exist = True
- try:
- uassoc = UserAssociation.objects.get(openid_url__exact = openid_url)
- except:
- is_exist = False
- logging.debug(str(is_exist))
- return is_exist
-
-@not_authenticated
-def register(request):
- """
- register an openid.
-
- If user is already a member he can associate its openid with
- its account.
-
- A new account could also be created and automaticaly associated
- to the openid.
-
- url : /complete/
-
- template : authopenid/complete.html
- """
-
- logging.debug('')
- openid_ = request.session.get('openid', None)
- next = get_next_url(request)
- if not openid_:
- logging.debug('oops, no openid in session --> go back to signin')
- return HttpResponseRedirect(reverse('user_signin') + '?next=%s' % next)
-
- nickname = openid_.sreg.get('nickname', '')
- email = openid_.sreg.get('email', '')
- form1 = OpenidRegisterForm(initial={
- 'next': next,
- 'username': nickname,
- 'email': email,
- })
- form2 = OpenidVerifyForm(initial={
- 'next': next,
- 'username': nickname,
- })
- email_feeds_form = SimpleEmailSubscribeForm()
-
- user_ = None
- is_redirect = False
- logging.debug('request method is %s' % request.method)
- if request.method == 'POST':
- if 'bnewaccount' in request.POST.keys():
- logging.debug('trying to create new account associated with openid')
- form1 = OpenidRegisterForm(request.POST)
- email_feeds_form = SimpleEmailSubscribeForm(request.POST)
- if not form1.is_valid():
- logging.debug('OpenidRegisterForm is INVALID')
- elif not email_feeds_form.is_valid():
- logging.debug('SimpleEmailSubscribeForm is INVALID')
- else:
- logging.debug('OpenidRegisterForm and SimpleEmailSubscribeForm are valid')
- next = form1.cleaned_data['next']
- is_redirect = True
- logging.debug('creatng new django user %s ...' % form1.cleaned_data['username'])
- tmp_pwd = User.objects.make_random_password()
- user_ = User.objects.create_user(form1.cleaned_data['username'],
- form1.cleaned_data['email'], tmp_pwd)
-
- user_.set_unusable_password()
- # make association with openid
- logging.debug('creating new openid user association %s <--> %s' \
- % (user_.username, str(openid_)))
- uassoc = UserAssociation(openid_url=str(openid_), user_id=user_.id)
- uassoc.save()
-
- # login
- user_.backend = "django.contrib.auth.backends.ModelBackend"
- logging.debug('logging the user in')
- login(request, user_)
- logging.debug('saving email feed settings')
- email_feeds_form.save(user_)
- elif 'bverify' in request.POST.keys():
- logging.debug('processing OpenidVerify form')
- form2 = OpenidVerifyForm(request.POST)
- if form2.is_valid():
- logging.debug('form is valid')
- is_redirect = True
- next = form2.cleaned_data['next']
- user_ = form2.get_user()
- logging.debug('creating new openid user association %s <--> %s' \
- % (user_.username, str(openid_)))
- uassoc = UserAssociation(openid_url=str(openid_),
- user_id=user_.id)
- uassoc.save()
- logging.debug('logging the user in')
- login(request, user_)
-
- #check if we need to post a question that was added anonymously
- #this needs to be a function call becase this is also done
- #if user just logged in and did not need to create the new account
-
- if user_ != None:
- if settings.EMAIL_VALIDATION == 'on':
- logging.debug('sending email validation')
- send_new_email_key(user_,nomessage=True)
- output = validation_email_sent(request)
- set_email_validation_message(user_) #message set after generating view
- return output
- if user_.is_authenticated():
- logging.debug('success, send user to main page')
- return HttpResponseRedirect(reverse('index'))
- else:
- logging.debug('have really strange error')
- raise Exception('openid login failed')#should not ever get here
-
- openid_str = str(openid_)
- bits = openid_str.split('/')
- base_url = bits[2] #assume this is base url
- url_bits = base_url.split('.')
- provider_name = url_bits[-2].lower()
-
- providers = {'yahoo':'<font color="purple">Yahoo!</font>',
- 'flickr':'<font color="#0063dc">flick</font><font color="#ff0084">r</font>&trade;',
- 'google':'Google&trade;',
- 'aol':'<font color="#31658e">AOL</font>',
- 'myopenid':'MyOpenID',
- }
- if provider_name not in providers:
- provider_logo = provider_name
- logging.error('openid provider named "%s" has no pretty customized logo' % provider_name)
- else:
- provider_logo = providers[provider_name]
-
- logging.debug('printing authopenid/complete.html output')
- return render_to_response('authopenid/complete.html', {
- 'form1': form1,
- 'form2': form2,
- 'email_feeds_form': email_feeds_form,
- 'provider':mark_safe(provider_logo),
- 'username': nickname,
- 'email': email,
- 'login_type':'openid',
- 'gravatar_faq_url':reverse('faq') + '#gravatar',
- }, context_instance=RequestContext(request))
-
-def signin_failure(request, message):
- """
- falure with openid signin. Go back to signin page.
-
- template : "authopenid/signin.html"
- """
- logging.debug('')
- next = get_next_url(request)
- form_signin = OpenidSigninForm(initial={'next': next})
- form_auth = ClassicLoginForm(initial={'next': next})
-
- return render_to_response('authopenid/signin.html', {
- 'msg': message,
- 'form1': form_auth,
- 'form2': form_signin,
- }, context_instance=RequestContext(request))
-
-@not_authenticated
-def signup(request):
- """
- signup page. Create a legacy account
-
- url : /signup/"
-
- templates: authopenid/signup.html, authopenid/confirm_email.txt
- """
- logging.debug('')
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- logging.debug('handling external legacy login registration')
- return HttpResponseRedirect(reverse('user_external_legacy_login_signup'))
- next = get_next_url(request)
- logging.debug('request method was %s' % request.method)
- if request.method == 'POST':
- form = ClassicRegisterForm(request.POST)
- email_feeds_form = SimpleEmailSubscribeForm(request.POST)
-
- #validation outside if to remember form values
- form1_is_valid = form.is_valid()
- form2_is_valid = email_feeds_form.is_valid()
- if form1_is_valid and form2_is_valid:
- next = form.cleaned_data['next']
- username = form.cleaned_data['username']
- password = form.cleaned_data['password1']
- email = form.cleaned_data['email']
-
- user_ = User.objects.create_user( username,email,password )
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- EXTERNAL_LOGIN_APP.api.create_user(username,email,password)
-
- user_.backend = "django.contrib.auth.backends.ModelBackend"
- login(request, user_)
- email_feeds_form.save(user_)
-
- # send email
- subject = _("Welcome email subject line")
- message_template = loader.get_template(
- 'authopenid/confirm_email.txt'
- )
- message_context = Context({
- 'signup_url': settings.APP_URL + reverse('user_signin'),
- 'username': username,
- 'password': password,
- })
- message = message_template.render(message_context)
- send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
- [user_.email])
- logging.debug('new user with login and password created!')
- return HttpResponseRedirect(next)
- else:
- logging.debug('create classic account forms were invalid')
- else:
- form = ClassicRegisterForm(initial={'next':next})
- email_feeds_form = SimpleEmailSubscribeForm()
- logging.debug('printing legacy signup form')
- return render_to_response('authopenid/signup.html', {
- 'form': form,
- 'email_feeds_form': email_feeds_form
- }, context_instance=RequestContext(request))
- #what if request is not posted?
-
-@login_required
-def signout(request):
- """
- signout from the website. Remove openid from session and kill it.
-
- url : /signout/"
- """
- logging.debug('')
- try:
- logging.debug('deleting openid session var')
- del request.session['openid']
- except KeyError:
- logging.debug('failed')
- pass
- logout(request)
- logging.debug('user logged out')
- return HttpResponseRedirect(get_next_url(request))
-
-def xrdf(request):
- url_host = get_url_host(request)
- logging.debug('what does this do??')
- return_to = [
- "%s%s" % (url_host, reverse('user_complete_signin'))
- ]
- return render_to_response('authopenid/yadis.xrdf', {
- 'return_to': return_to
- }, context_instance=RequestContext(request))
-
-@login_required
-def account_settings(request):
- """
- index pages to changes some basic account settings :
- - change password
- - change email
- - associate a new openid
- - delete account
-
- url : /
-
- template : authopenid/settings.html
- """
- logging.debug('')
- msg = request.GET.get('msg', '')
- is_openid = True
-
- try:
- uassoc = UserAssociation.objects.get(
- user__username__exact=request.user.username
- )
- except:
- is_openid = False
-
-
- return render_to_response('authopenid/settings.html', {
- 'msg': msg,
- 'is_openid': is_openid
- }, context_instance=RequestContext(request))
-
-@login_required
-def changepw(request):
- """
- change password view.
-
- url : /changepw/
- template: authopenid/changepw.html
- """
- logging.debug('')
- user_ = request.user
-
- if user_.has_usable_password():
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
- else:
- raise Http404
-
- if request.POST:
- form = ChangePasswordForm(request.POST, user=user_)
- if form.is_valid():
- user_.set_password(form.cleaned_data['password1'])
- user_.save()
- msg = _("Password changed.")
- redirect = "%s?msg=%s" % (
- reverse('user_account_settings'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
- else:
- form = ChangePasswordForm(user=user_)
-
- return render_to_response('authopenid/changepw.html', {'form': form },
- context_instance=RequestContext(request))
-
-def find_email_validation_messages(user):
- msg_text = _('your email needs to be validated see %(details_url)s') \
- % {'details_url':reverse('faq') + '#validate'}
- return user.message_set.filter(message__exact=msg_text)
-
-def set_email_validation_message(user):
- messages = find_email_validation_messages(user)
- msg_text = _('your email needs to be validated see %(details_url)s') \
- % {'details_url':reverse('faq') + '#validate'}
- if len(messages) == 0:
- user.message_set.create(message=msg_text)
-
-def clear_email_validation_message(user):
- messages = find_email_validation_messages(user)
- messages.delete()
-
-def set_new_email(user, new_email, nomessage=False):
- if new_email != user.email:
- user.email = new_email
- user.email_isvalid = False
- user.save()
- if settings.EMAIL_VALIDATION == 'on':
- send_new_email_key(user,nomessage=nomessage)
-
-def _send_email_key(user):
- """private function. sends email containing validation key
- to user's email address
- """
- subject = _("Email verification subject line")
- message_template = loader.get_template('authopenid/email_validation.txt')
- import settings
- message_context = Context({
- 'validation_link': settings.APP_URL + reverse('user_verifyemail', kwargs={'id':user.id,'key':user.email_key})
- })
- message = message_template.render(message_context)
- send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])
-
-def send_new_email_key(user,nomessage=False):
- import random
- random.seed()
- user.email_key = '%032x' % random.getrandbits(128)
- user.save()
- _send_email_key(user)
- if nomessage==False:
- set_email_validation_message(user)
-
-@login_required
-def send_email_key(request):
- """
- url = /email/sendkey/
-
- view that is shown right after sending email key
- email sending is called internally
-
- raises 404 if email validation is off
- if current email is valid shows 'key_not_sent' view of
- authopenid/changeemail.html template
- """
-
- if settings.EMAIL_VALIDATION != 'off':
- if request.user.email_isvalid:
- return render_to_response('authopenid/changeemail.html',
- { 'email': request.user.email,
- 'action_type': 'key_not_sent',
- 'change_link': reverse('user_changeemail')},
- context_instance=RequestContext(request)
- )
- else:
- send_new_email_key(request.user)
- return validation_email_sent(request)
- else:
- raise Http404
-
-
-#internal server view used as return value by other views
-def validation_email_sent(request):
- logging.debug('')
- return render_to_response('authopenid/changeemail.html',
- { 'email': request.user.email,
- 'change_email_url': reverse('user_changeemail'),
- 'action_type': 'validate', },
- context_instance=RequestContext(request))
-
-def verifyemail(request,id=None,key=None):
- """
- view that is shown when user clicks email validation link
- url = /email/verify/{{user.id}}/{{user.email_key}}/
- """
- logging.debug('')
- if settings.EMAIL_VALIDATION != 'off':
- user = User.objects.get(id=id)
- if user:
- if user.email_key == key:
- user.email_isvalid = True
- clear_email_validation_message(user)
- user.save()
- return render_to_response('authopenid/changeemail.html', {
- 'action_type': 'validation_complete',
- }, context_instance=RequestContext(request))
- else:
- logging.error('hmm, no user found for email validation message - foul play?')
- raise Http404
-
-@login_required
-def changeemail(request, action='change'):
- """
- changeemail view. requires openid with request type GET
-
- url: /email/*
-
- template : authopenid/changeemail.html
- """
- logging.debug('')
- msg = request.GET.get('msg', None)
- extension_args = {}
- user_ = request.user
-
- if request.POST:
- if 'cancel' in request.POST:
- msg = _('your email was not changed')
- request.user.message_set.create(message=msg)
- return HttpResponseRedirect(get_next_url(request))
- form = ChangeEmailForm(request.POST, user=user_)
- if form.is_valid():
- new_email = form.cleaned_data['email']
- if new_email != user_.email:
- if settings.EMAIL_VALIDATION == 'on':
- action = 'validate'
- else:
- action = 'done_novalidate'
- set_new_email(user_, new_email,nomessage=True)
- else:
- action = 'keep'
-
- elif not request.POST and 'openid.mode' in request.GET:
- redirect_to = get_url_host(request) + reverse('user_changeemail')
- return complete(request, emailopenid_success,
- emailopenid_failure, redirect_to)
- else:
- form = ChangeEmailForm(initial={'email': user_.email},
- user=user_)
-
- output = render_to_response('authopenid/changeemail.html', {
- 'form': form,
- 'email': user_.email,
- 'action_type': action,
- 'gravatar_faq_url': reverse('faq') + '#gravatar',
- 'change_email_url': reverse('user_changeemail'),
- 'msg': msg
- }, context_instance=RequestContext(request))
-
- if action == 'validate':
- set_email_validation_message(user_)
-
- return output
-
-def emailopenid_success(request, identity_url, openid_response):
- logging.debug('')
- openid_ = from_openid_response(openid_response)
-
- user_ = request.user
- try:
- uassoc = UserAssociation.objects.get(
- openid_url__exact=identity_url
- )
- except:
- return emailopenid_failure(request,
- _("No OpenID %s found associated in our database" % identity_url))
-
- if uassoc.user.username != request.user.username:
- return emailopenid_failure(request,
- _("The OpenID %s isn't associated to current user logged in" %
- identity_url))
-
- new_email = request.session.get('new_email', '')
- if new_email:
- user_.email = new_email
- user_.save()
- del request.session['new_email']
- msg = _("Email Changed.")
-
- redirect = "%s?msg=%s" % (reverse('user_account_settings'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
-
-def emailopenid_failure(request, message):
- logging.debug('')
- redirect_to = "%s?msg=%s" % (
- reverse('user_changeemail'), urlquote_plus(message))
- return HttpResponseRedirect(redirect_to)
-
-@login_required
-def changeopenid(request):
- """
- change openid view. Allow user to change openid
- associated to its username.
-
- url : /changeopenid/
-
- template: authopenid/changeopenid.html
- """
- logging.error('change openid view - never tested it yet!!!')
-
- extension_args = {}
- openid_url = ''
- has_openid = True
- msg = request.GET.get('msg', '')
-
- user_ = request.user
-
- try:
- uopenid = UserAssociation.objects.get(user=user_)
- openid_url = uopenid.openid_url
- except:
- has_openid = False
-
- redirect_to = get_url_host(request) + reverse('user_changeopenid')
- if request.POST and has_openid:
- form = ChangeopenidForm(request.POST, user=user_)
- if form.is_valid():
- return ask_openid(request, form.cleaned_data['openid_url'],
- redirect_to, on_failure=changeopenid_failure)
- elif not request.POST and has_openid:
- if 'openid.mode' in request.GET:
- return complete(request, changeopenid_success,
- changeopenid_failure, redirect_to)
-
- form = ChangeopenidForm(initial={'openid_url': openid_url }, user=user_)
- return render_to_response('authopenid/changeopenid.html', {
- 'form': form,
- 'has_openid': has_openid,
- 'msg': msg
- }, context_instance=RequestContext(request))
-
-def changeopenid_success(request, identity_url, openid_response):
- logging.error('never tested this worflow')
- openid_ = from_openid_response(openid_response)
- is_exist = True
- try:
- uassoc = UserAssociation.objects.get(openid_url__exact=identity_url)
- except:
- is_exist = False
-
- if not is_exist:
- try:
- uassoc = UserAssociation.objects.get(
- user__username__exact=request.user.username
- )
- uassoc.openid_url = identity_url
- uassoc.save()
- except:
- uassoc = UserAssociation(user=request.user,
- openid_url=identity_url)
- uassoc.save()
- elif uassoc.user.username != request.user.username:
- return changeopenid_failure(request,
- _('This OpenID is already associated with another account.'))
-
- request.session['openids'] = []
- request.session['openids'].append(openid_)
-
- msg = _("OpenID %s is now associated with your account." % identity_url)
- redirect = "%s?msg=%s" % (
- reverse('user_account_settings'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
-
-def changeopenid_failure(request, message):
- logging.error('never tested this workflow')
- redirect_to = "%s?msg=%s" % (
- reverse('user_changeopenid'),
- urlquote_plus(message))
- return HttpResponseRedirect(redirect_to)
-
-@login_required
-def delete(request):
- """
- delete view. Allow user to delete its account. Password/openid are required to
- confirm it. He should also check the confirm checkbox.
-
- url : /delete
-
- template : authopenid/delete.html
- """
- logging.error('deleting account - never tested this')
-
- extension_args = {}
-
- user_ = request.user
-
- redirect_to = get_url_host(request) + reverse('user_delete')
- if request.POST:
- form = DeleteForm(request.POST, user=user_)
- if form.is_valid():
- if not form.test_openid:
- user_.delete()
- return signout(request)
- else:
- return ask_openid(request, form.cleaned_data['password'],
- redirect_to, on_failure=deleteopenid_failure)
- elif not request.POST and 'openid.mode' in request.GET:
- return complete(request, deleteopenid_success, deleteopenid_failure,
- redirect_to)
-
- form = DeleteForm(user=user_)
-
- msg = request.GET.get('msg','')
- return render_to_response('authopenid/delete.html', {
- 'form': form,
- 'msg': msg,
- }, context_instance=RequestContext(request))
-
-def deleteopenid_success(request, identity_url, openid_response):
- logging.error('never tested this')
- openid_ = from_openid_response(openid_response)
-
- user_ = request.user
- try:
- uassoc = UserAssociation.objects.get(
- openid_url__exact=identity_url
- )
- except:
- return deleteopenid_failure(request,
- _("No OpenID %s found associated in our database" % identity_url))
-
- if uassoc.user.username == user_.username:
- user_.delete()
- return signout(request)
- else:
- return deleteopenid_failure(request,
- _("The OpenID %s isn't associated to current user logged in" %
- identity_url))
-
- msg = _("Account deleted.")
- redirect = reverse('index') + u"/?msg=%s" % (urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
-
-def deleteopenid_failure(request, message):
- logging.error('never tested this')
- redirect_to = "%s?msg=%s" % (reverse('user_delete'), urlquote_plus(message))
- return HttpResponseRedirect(redirect_to)
-
-def external_legacy_login_info(request):
- logging.debug('maybe this view does not belong in this library')
- feedback_url = reverse('feedback')
- return render_to_response('authopenid/external_legacy_login_info.html',
- {'feedback_url':feedback_url},
- context_instance=RequestContext(request))
-
-def sendpw(request):
- """
- send a new password to the user. It return a mail with
- a new pasword and a confirm link in. To activate the
- new password, the user should click on confirm link.
-
- url : /sendpw/
-
- templates : authopenid/sendpw_email.txt, authopenid/sendpw.html
- """
- logging.debug('')
- if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
- logging.debug('delegating to view dealing with external password recovery')
- return HttpResponseRedirect(reverse('user_external_legacy_login_issues'))
-
- msg = request.GET.get('msg','')
- logging.debug('request method is %s' % request.method)
- if request.method == 'POST':
- form = EmailPasswordForm(request.POST)
- if form.is_valid():
- logging.debug('EmailPasswordForm is valid')
- new_pw = User.objects.make_random_password()
- confirm_key = UserPasswordQueue.objects.get_new_confirm_key()
- try:
- uqueue = UserPasswordQueue.objects.get(
- user=form.user_cache
- )
- except:
- uqueue = UserPasswordQueue(
- user=form.user_cache
- )
- uqueue.new_password = new_pw
- uqueue.confirm_key = confirm_key
- uqueue.save()
- # send email
- subject = _("Request for new password")
- message_template = loader.get_template(
- 'authopenid/sendpw_email.txt')
- key_link = settings.APP_URL + reverse('user_confirmchangepw') + '?key=' + confirm_key
- logging.debug('emailing new password for %s' % form.user_cache.username)
- message_context = Context({
- 'site_url': settings.APP_URL + reverse('index'),
- 'key_link': key_link,
- 'username': form.user_cache.username,
- 'password': new_pw,
- })
- message = message_template.render(message_context)
- send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
- [form.user_cache.email])
- msg = _("A new password and the activation link were sent to your email address.")
- else:
- form = EmailPasswordForm()
-
- logging.debug('showing reset password form')
- return render_to_response('authopenid/sendpw.html', {
- 'form': form,
- 'msg': msg
- }, context_instance=RequestContext(request))
-
-def confirmchangepw(request):
- """
- view to set new password when the user click on confirm link
- in its mail. Basically it check if the confirm key exist, then
- replace old password with new password and remove confirm
- ley from the queue. Then it redirect the user to signin
- page.
-
- url : /sendpw/confirm/?key
-
- """
- logging.debug('')
- confirm_key = request.GET.get('key', '')
- if not confirm_key:
- logging.error('someone called confirm password without a key!')
- return HttpResponseRedirect(reverse('index'))
-
- try:
- uqueue = UserPasswordQueue.objects.get(
- confirm_key__exact=confirm_key
- )
- except:
- msg = _("Could not change password. Confirmation key '%s'\
- is not registered." % confirm_key)
- logging.error(msg)
- redirect = "%s?msg=%s" % (
- reverse('user_sendpw'), urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
- try:
- user_ = User.objects.get(id=uqueue.user.id)
- except:
- msg = _("Can not change password. User don't exist anymore \
- in our database.")
- logging.error(msg)
- redirect = "%s?msg=%s" % (reverse('user_sendpw'),
- urlquote_plus(msg))
- return HttpResponseRedirect(redirect)
-
- user_.set_password(uqueue.new_password)
- user_.save()
- uqueue.delete()
- msg = _("Password changed for %s. You may now sign in." %
- user_.username)
- logging.debug(msg)
- redirect = "%s?msg=%s" % (reverse('user_signin'),
- urlquote_plus(msg))
-
- return HttpResponseRedirect(redirect)
diff --git a/fbconnect/fb.py b/fbconnect/fb.py
deleted file mode 100755
index afcd8210..00000000
--- a/fbconnect/fb.py
+++ /dev/null
@@ -1,96 +0,0 @@
-from django.conf import settings
-from time import time
-from datetime import datetime
-from urllib import urlopen, urlencode
-
-try:
- from json import load as load_json
-except:
- from pjson import fread as load_json
-
-from models import FBAssociation
-import hashlib
-import logging
-
-REST_SERVER = 'http://api.facebook.com/restserver.php'
-
-def generate_sig(values):
- keys = []
-
- for key in sorted(values.keys()):
- keys.append(key)
-
- signature = ''.join(['%s=%s' % (key, values[key]) for key in keys]) + settings.FB_SECRET
- return hashlib.md5(signature).hexdigest()
-
-def check_cookies_signature(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 generate_sig(values) == cookies[API_KEY]
-
-def get_user_data(cookies):
- request_data = {
- 'method': 'Users.getInfo',
- 'api_key': settings.FB_API_KEY,
- 'call_id': time(),
- 'v': '1.0',
- 'uids': cookies[settings.FB_API_KEY + '_user'],
- 'fields': 'name,first_name,last_name',
- 'format': 'json',
- }
-
- request_data['sig'] = generate_sig(request_data)
- fb_response = urlopen(REST_SERVER, urlencode(request_data))
- #print(fb_response)
- return load_json(fb_response)[0]
-
-
-def delete_cookies(response):
- API_KEY = settings.FB_API_KEY
-
- response.delete_cookie(API_KEY + '_user')
- response.delete_cookie(API_KEY + '_session_key')
- response.delete_cookie(API_KEY + '_expires')
- response.delete_cookie(API_KEY + '_ss')
- response.delete_cookie(API_KEY)
- response.delete_cookie('fbsetting_' + API_KEY)
-
-def check_session_expiry(cookies):
- return datetime.fromtimestamp(float(cookies[settings.FB_API_KEY+'_expires'])) > datetime.now()
-
-STATES = {
- 'FIRSTTIMER': 1,
- 'SESSIONEXPIRED': 2,
- 'RETURNINGUSER': 3,
- 'INVALIDSTATE': 4,
-}
-
-def get_user_state(request):
- API_KEY = settings.FB_API_KEY
- logging.debug('')
-
- if API_KEY in request.COOKIES:
- logging.debug('FB API key is in request cookies')
- if check_cookies_signature(request.COOKIES):
- logging.debug('FB cookie signature is fine')
- if check_session_expiry(request.COOKIES):
- logging.debug('FB session is not expired')
- try:
- uassoc = FBAssociation.objects.get(fbuid=request.COOKIES[API_KEY + '_user'])
- logging.debug('found existing FB user association')
- return (STATES['RETURNINGUSER'], uassoc.user)
- except:
- logging.debug('dont have FB association for this user')
- return (STATES['FIRSTTIMER'], get_user_data(request.COOKIES))
- else:
- logging.debug('FB session expired')
- return (STATES['SESSIONEXPIRED'], None)
- logging.debug('FB state is INVALID')
-
- return (STATES['INVALIDSTATE'], None)
diff --git a/fbconnect/forms.py b/fbconnect/forms.py
deleted file mode 100755
index 94f86816..00000000
--- a/fbconnect/forms.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from django_authopenid.forms import NextUrlField, UserNameField, UserEmailField
-
-from django import forms
-
-class FBConnectRegisterForm(forms.Form):
- next = NextUrlField()
- username = UserNameField()
- email = UserEmailField()
diff --git a/fbconnect/models.py b/fbconnect/models.py
deleted file mode 100755
index 2172217d..00000000
--- a/fbconnect/models.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django.db import models
-from django.contrib.auth.models import User
-
-class FBAssociation(models.Model):
- user = models.ForeignKey(User)
- fbuid = models.CharField(max_length=12, unique=True)
diff --git a/fbconnect/pjson.py b/fbconnect/pjson.py
deleted file mode 100755
index 273b684e..00000000
--- a/fbconnect/pjson.py
+++ /dev/null
@@ -1,313 +0,0 @@
-import string
-import types
-
-## json.py implements a JSON (http://json.org) reader and writer.
-## Copyright (C) 2005 Patrick D. Logan
-## Contact mailto:patrickdlogan@stardecisions.com
-##
-## This library is free software; you can redistribute it and/or
-## modify it under the terms of the GNU Lesser General Public
-## License as published by the Free Software Foundation; either
-## version 2.1 of the License, or (at your option) any later version.
-##
-## This library is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-## Lesser General Public License for more details.
-##
-## You should have received a copy of the GNU Lesser General Public
-## License along with this library; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-class _StringGenerator(object):
- def __init__(self, string):
- self.string = string
- self.index = -1
- def peek(self):
- i = self.index + 1
- if i < len(self.string):
- return self.string[i]
- else:
- return None
- def next(self):
- self.index += 1
- if self.index < len(self.string):
- return self.string[self.index]
- else:
- raise StopIteration
- def all(self):
- return self.string
-
-class WriteException(Exception):
- pass
-
-class ReadException(Exception):
- pass
-
-class JsonReader(object):
- hex_digits = {'A': 10,'B': 11,'C': 12,'D': 13,'E': 14,'F':15}
- escapes = {'t':'\t','n':'\n','f':'\f','r':'\r','b':'\b'}
-
- def read(self, s):
- self._generator = _StringGenerator(s)
- result = self._read()
- return result
-
- def _read(self):
- self._eatWhitespace()
- peek = self._peek()
- if peek is None:
- raise ReadException, "Nothing to read: '%s'" % self._generator.all()
- if peek == '{':
- return self._readObject()
- elif peek == '[':
- return self._readArray()
- elif peek == '"':
- return self._readString()
- elif peek == '-' or peek.isdigit():
- return self._readNumber()
- elif peek == 't':
- return self._readTrue()
- elif peek == 'f':
- return self._readFalse()
- elif peek == 'n':
- return self._readNull()
- elif peek == '/':
- self._readComment()
- return self._read()
- else:
- raise ReadException, "Input is not valid JSON: '%s'" % self._generator.all()
-
- def _readTrue(self):
- self._assertNext('t', "true")
- self._assertNext('r', "true")
- self._assertNext('u', "true")
- self._assertNext('e', "true")
- return True
-
- def _readFalse(self):
- self._assertNext('f', "false")
- self._assertNext('a', "false")
- self._assertNext('l', "false")
- self._assertNext('s', "false")
- self._assertNext('e', "false")
- return False
-
- def _readNull(self):
- self._assertNext('n', "null")
- self._assertNext('u', "null")
- self._assertNext('l', "null")
- self._assertNext('l', "null")
- return None
-
- def _assertNext(self, ch, target):
- if self._next() != ch:
- raise ReadException, "Trying to read %s: '%s'" % (target, self._generator.all())
-
- def _readNumber(self):
- isfloat = False
- result = self._next()
- peek = self._peek()
- while peek is not None and (peek.isdigit() or peek == "."):
- isfloat = isfloat or peek == "."
- result = result + self._next()
- peek = self._peek()
- try:
- if isfloat:
- return float(result)
- else:
- return int(result)
- except ValueError:
- raise ReadException, "Not a valid JSON number: '%s'" % result
-
- def _readString(self):
- result = ""
- assert self._next() == '"'
- try:
- while self._peek() != '"':
- ch = self._next()
- if ch == "\\":
- ch = self._next()
- if ch in 'brnft':
- ch = self.escapes[ch]
- elif ch == "u":
- ch4096 = self._next()
- ch256 = self._next()
- ch16 = self._next()
- ch1 = self._next()
- n = 4096 * self._hexDigitToInt(ch4096)
- n += 256 * self._hexDigitToInt(ch256)
- n += 16 * self._hexDigitToInt(ch16)
- n += self._hexDigitToInt(ch1)
- ch = unichr(n)
- elif ch not in '"/\\':
- raise ReadException, "Not a valid escaped JSON character: '%s' in %s" % (ch, self._generator.all())
- result = result + ch
- except StopIteration:
- raise ReadException, "Not a valid JSON string: '%s'" % self._generator.all()
- assert self._next() == '"'
- return result
-
- def _hexDigitToInt(self, ch):
- try:
- result = self.hex_digits[ch.upper()]
- except KeyError:
- try:
- result = int(ch)
- except ValueError:
- raise ReadException, "The character %s is not a hex digit." % ch
- return result
-
- def _readComment(self):
- assert self._next() == "/"
- second = self._next()
- if second == "/":
- self._readDoubleSolidusComment()
- elif second == '*':
- self._readCStyleComment()
- else:
- raise ReadException, "Not a valid JSON comment: %s" % self._generator.all()
-
- def _readCStyleComment(self):
- try:
- done = False
- while not done:
- ch = self._next()
- done = (ch == "*" and self._peek() == "/")
- if not done and ch == "/" and self._peek() == "*":
- raise ReadException, "Not a valid JSON comment: %s, '/*' cannot be embedded in the comment." % self._generator.all()
- self._next()
- except StopIteration:
- raise ReadException, "Not a valid JSON comment: %s, expected */" % self._generator.all()
-
- def _readDoubleSolidusComment(self):
- try:
- ch = self._next()
- while ch != "\r" and ch != "\n":
- ch = self._next()
- except StopIteration:
- pass
-
- def _readArray(self):
- result = []
- assert self._next() == '['
- done = self._peek() == ']'
- while not done:
- item = self._read()
- result.append(item)
- self._eatWhitespace()
- done = self._peek() == ']'
- if not done:
- ch = self._next()
- if ch != ",":
- raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
- assert ']' == self._next()
- return result
-
- def _readObject(self):
- result = {}
- assert self._next() == '{'
- done = self._peek() == '}'
- while not done:
- key = self._read()
- if type(key) is not types.StringType:
- raise ReadException, "Not a valid JSON object key (should be a string): %s" % key
- self._eatWhitespace()
- ch = self._next()
- if ch != ":":
- raise ReadException, "Not a valid JSON object: '%s' due to: '%s'" % (self._generator.all(), ch)
- self._eatWhitespace()
- val = self._read()
- result[key] = val
- self._eatWhitespace()
- done = self._peek() == '}'
- if not done:
- ch = self._next()
- if ch != ",":
- raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
- assert self._next() == "}"
- return result
-
- def _eatWhitespace(self):
- p = self._peek()
- while p is not None and p in string.whitespace or p == '/':
- if p == '/':
- self._readComment()
- else:
- self._next()
- p = self._peek()
-
- def _peek(self):
- return self._generator.peek()
-
- def _next(self):
- return self._generator.next()
-
-class JsonWriter(object):
-
- def _append(self, s):
- self._results.append(s)
-
- def write(self, obj, escaped_forward_slash=False):
- self._escaped_forward_slash = escaped_forward_slash
- self._results = []
- self._write(obj)
- return "".join(self._results)
-
- def _write(self, obj):
- ty = type(obj)
- if ty is types.DictType:
- n = len(obj)
- self._append("{")
- for k, v in obj.items():
- self._write(k)
- self._append(":")
- self._write(v)
- n = n - 1
- if n > 0:
- self._append(",")
- self._append("}")
- elif ty is types.ListType or ty is types.TupleType:
- n = len(obj)
- self._append("[")
- for item in obj:
- self._write(item)
- n = n - 1
- if n > 0:
- self._append(",")
- self._append("]")
- elif ty is types.StringType or ty is types.UnicodeType:
- self._append('"')
- obj = obj.replace('\\', r'\\')
- if self._escaped_forward_slash:
- obj = obj.replace('/', r'\/')
- obj = obj.replace('"', r'\"')
- obj = obj.replace('\b', r'\b')
- obj = obj.replace('\f', r'\f')
- obj = obj.replace('\n', r'\n')
- obj = obj.replace('\r', r'\r')
- obj = obj.replace('\t', r'\t')
- self._append(obj)
- self._append('"')
- elif ty is types.IntType or ty is types.LongType:
- self._append(str(obj))
- elif ty is types.FloatType:
- self._append("%f" % obj)
- elif obj is True:
- self._append("true")
- elif obj is False:
- self._append("false")
- elif obj is None:
- self._append("null")
- else:
- raise WriteException, "Cannot write in JSON: %s" % repr(obj)
-
-def write(obj, escaped_forward_slash=False):
- return JsonWriter().write(obj, escaped_forward_slash)
-
-def read(s):
- return JsonReader().read(s)
-
-def fread(f):
- return read(f.read())
diff --git a/fbconnect/tests.py b/fbconnect/tests.py
deleted file mode 100755
index 2247054b..00000000
--- a/fbconnect/tests.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-This file demonstrates two different styles of tests (one doctest and one
-unittest). These will both pass when you run "manage.py test".
-
-Replace these with more appropriate tests for your application.
-"""
-
-from django.test import TestCase
-
-class SimpleTest(TestCase):
- def test_basic_addition(self):
- """
- Tests that 1 + 1 always equals 2.
- """
- self.failUnlessEqual(1 + 1, 2)
-
-__test__ = {"doctest": """
-Another way to test that 1 + 1 is equal to 2.
-
->>> 1 + 1 == 2
-True
-"""}
-
diff --git a/fbconnect/urls.py b/fbconnect/urls.py
deleted file mode 100755
index bf2d4364..00000000
--- a/fbconnect/urls.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from django.conf.urls.defaults import *
-from django.utils.translation import ugettext as _
-from django.views.generic.simple import direct_to_template
-from django.conf import settings
-from views import signin, register
-
-urlpatterns = patterns('',
- url(r'^xd_receiver$', direct_to_template, {'template': 'fbconnect/xd_receiver.html',\
- 'extra_context': {'APP_SHORT_NAME':settings.APP_SHORT_NAME}},\
- name='xd_receiver'),
-
- url(r'^%s$' % _('signin/'), signin, name="fb_signin"),
- url(r'^%s%s$' % (_('signin/'), _('newquestion/')), signin, {'newquestion': True}, name="fb_signin_new_question"),
- url(r'^%s%s$' % (_('signin/'), _('newanswer/')), signin, {'newanswer': True}, name="fb_signin_new_answer"),
-
- url(r'^%s$' % _('register/'), register, name="fb_user_register"),
- url(r'^%s%s$' % (_('register/'), _('newquestion/')), register, {'newquestion': True}, name="fb_user_register_new_question"),
- url(r'^%s%s$' % (_('register/'), _('newanswer/')), register, {'newanswer': True}, name="fb_user_register_new_answer"),
-)
diff --git a/fbconnect/views.py b/fbconnect/views.py
deleted file mode 100755
index 1781f6bf..00000000
--- a/fbconnect/views.py
+++ /dev/null
@@ -1,112 +0,0 @@
-from django.shortcuts import render_to_response as render
-from django.template import RequestContext
-from django.http import HttpResponseRedirect
-from django.utils.safestring import mark_safe
-from django.core.urlresolvers import reverse
-from django.contrib.auth.models import User
-from django.contrib.auth import login, logout
-from models import FBAssociation
-from forum.forms import SimpleEmailSubscribeForm
-from django.conf import settings
-
-import fb
-import forms
-
-import logging
-
-def signin(request, newquestion = False, newanswer = False):
- logging.debug('')
- state, context = fb.get_user_state(request)
-
- if state == fb.STATES['FIRSTTIMER']:
- logging.debug('FB state = FIRSTTIMER')
- if newquestion:
- register_url = 'fb_user_register_new_question'
- elif newanswer:
- register_url = 'fb_user_register_new_answer'
- else:
- register_url = 'fb_user_register'
- return HttpResponseRedirect(reverse(register_url))
- elif state == fb.STATES['RETURNINGUSER']:
- logging.debug('FB state = RETURNINGUSER')
- return login_and_forward(request, context, newquestion, newanswer)
- elif state == fb.STATES['SESSIONEXPIRED']:
- logging.debug('FB state = SESSIONEXPIRED')
- response = logout(request, next_page=reverse('index'))
- fb.delete_cookies(response)
- return response
-
- return HttpResponseRedirect(reverse('index'))
-
-def register(request, newquestion = False, newanswer = False):
- logging.debug('')
- state, context = fb.get_user_state(request)
-
- if state == fb.STATES['FIRSTTIMER']:
- logging.debug('FB FIRSTTIMER - try to register locally')
- logging.debug('request method is %s' % request.method)
- if request.method == 'POST' and 'bnewaccount' in request.POST:
- form1 = forms.FBConnectRegisterForm(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()
- logging.debug('created new internal user %s' % form1.cleaned_data['username'])
-
- uassoc = FBAssociation(user=user_, fbuid=context['uid'])
- uassoc.save()
- logging.debug('created new user association')
-
- email_feeds_form.save(user_)
-
- return login_and_forward(request, user_, newquestion, newanswer)
- else:
- logging.debug('form user input is invalid')
- else:
- form1 = forms.FBConnectRegisterForm(initial={
- 'next': '/',
- 'username': context['name'],
- 'email': '',
- })
- email_feeds_form = SimpleEmailSubscribeForm()
-
- return render('authopenid/complete.html', {
- 'form1': form1,
- 'email_feeds_form': email_feeds_form,
- 'provider':mark_safe('facebook'),
- 'login_type':'facebook',
- 'gravatar_faq_url':reverse('faq') + '#gravatar',
- }, context_instance=RequestContext(request))
- else:
- logging.debug('not a FIRSTTIMER --> redirect to index view')
- return HttpResponseRedirect(reverse('index'))
-
-def login_and_forward(request, user, newquestion = False, newanswer = False):
- old_session = request.session.session_key
- user.backend = "django.contrib.auth.backends.ModelBackend"
- logging.debug('attached auth.backends.ModelBackend to this FB user')
- login(request, user)
- logging.debug('user logged in!')
-
- from forum.models import user_logged_in
- user_logged_in.send(user=user,session_key=old_session,sender=None)
- logging.debug('user_logged_in signal sent')
-
- if (newquestion):
- from forum.models import Question
- question = Question.objects.filter(author=user).order_by('-added_at')[0]
- logging.debug('redirecting to newly posted question')
- return HttpResponseRedirect(question.get_absolute_url())
-
- if (newanswer):
- from forum.models import Answer
- answer = Answer.objects.filter(author=user).order_by('-added_at')[0]
- logging.debug('redirecting to newly posted answer')
- return HttpResponseRedirect(answer.get_absolute_url())
-
- logging.debug('redirecting to front page')
- return HttpResponseRedirect('/')
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 3afa2241..3afa2241 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..5326c45c
--- /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..08534adc
--- /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..019c85f3
--- /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 ce81acb2..ce81acb2 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 08ce8738..5d4fb516 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
from django.contrib.contenttypes.models import ContentType
@@ -260,6 +262,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')))
@@ -291,7 +312,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
@@ -337,21 +358,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 b654caaa..b654caaa 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 c476e9c9..c476e9c9 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 56d5a4e5..db66a227 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
import re
@@ -339,6 +339,7 @@ Repute = Repute
Activity = Activity
EmailFeedSetting = EmailFeedSetting
AnonymousEmail = AnonymousEmail
+AuthKeyUserAssociation = AuthKeyUserAssociation
__all__ = [
'Question',
@@ -364,6 +365,7 @@ __all__ = [
'Activity',
'EmailFeedSetting',
'AnonymousEmail',
+ 'AuthKeyUserAssociation',
'User'
]
diff --git a/forum/models/answer.py b/forum/models/answer.py
index 16e55c69..9d11bb5f 100755
--- a/forum/models/answer.py
+++ b/forum/models/answer.py
@@ -3,7 +3,7 @@ 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):
+ 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 37924a5a..ea26cace 100755
--- a/forum/models/question.py
+++ b/forum/models/question.py
@@ -2,7 +2,7 @@ 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):
+ 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/repute.py b/forum/models/repute.py
index 149f656f..f0ee27ed 100755
--- a/forum/models/repute.py
+++ b/forum/models/repute.py
@@ -85,7 +85,7 @@ class ReputeManager(models.Manager):
today = datetime.date.today()
sums = self.filter(models.Q(reputation_type=1) | models.Q(reputation_type=-8),
user=user, reputed_at__range=(today, today + datetime.timedelta(1))). \
- agregate(models.Sum('positive'), models.SUM('negative'))
+ agregate(models.Sum('positive'), models.Sum('negative'))
return sums['positive__sum'] + sums['negative__sum']
else:
diff --git a/forum/models/user.py b/forum/models/user.py
index fde1bbb0..bed6f55e 100755
--- a/forum/models/user.py
+++ b/forum/models/user.py
@@ -66,4 +66,11 @@ class AnonymousEmail(models.Model):
class Meta:
app_label = 'forum'
+class AuthKeyUserAssociation(models.Model):
+ key = models.CharField(max_length=255,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'
diff --git a/forum/modules.py b/forum/modules.py
index 26c4d50a..f786c719 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
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..33702758
--- /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 2f95921d..2f95921d 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 c2d74eca..c2d74eca 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 3644fdc3..3644fdc3 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 6341392e..6341392e 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..cf27f1bb
--- /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..c4ce5ddf 100644
--- a/forum/views/readers.py
+++ b/forum/views/readers.py
@@ -87,7 +87,7 @@ def index(request):#generates front page - shows listing of questions sorted in
}
view_id, orderby = _get_and_remember_questions_sort_method(request, view_dic, 'latest')
- pagesize = request.session.get("pagesize",QUESTIONS_PAGE_SIZE)
+ pagesize = QUESTIONS_PAGE_SIZE #request.session.get("pagesize",QUESTIONS_PAGE_SIZE)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
@@ -145,7 +145,7 @@ def questions(request, tagname=None, unanswered=False):#a view generating listin
# Set flag to False by default. If it is equal to True, then need to be saved.
pagesize_changed = False
# get pagesize from session, if failed then get default value
- pagesize = request.session.get("pagesize",QUESTIONS_PAGE_SIZE)
+ pagesize = QUESTIONS_PAGE_SIZE #request.session.get("pagesize",QUESTIONS_PAGE_SIZE)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
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/fbconnect/__init__.py b/forum_modules/facebookauth/__init__.py
index e69de29b..e69de29b 100755
--- a/fbconnect/__init__.py
+++ 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..512367a3
--- /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..67bf80c1
--- /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..cbe3b6c7
--- /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..d1d72c5d
--- /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..770ea08f
--- /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..8afa2b05
--- /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..aeebc40a
--- /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..1d1e0b3d
--- /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..04145461
--- /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..74734145
--- /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..89abf858
--- /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..67567b63
--- /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..c04c44b9
--- /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..68035968
--- /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..d76902df
--- /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..f7c641a1
--- /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..fa61ea6d
--- /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
diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po
index 50dbc597..e8cf9ba3 100644
--- a/locale/en/LC_MESSAGES/django.po
+++ b/locale/en/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-02-28 21:53-0500\n"
+"POT-Creation-Date: 2010-03-01 19:51-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,232 +16,6 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: django_authopenid/forms.py:71 django_authopenid/views.py:118
-msgid "i-names are not supported"
-msgstr ""
-
-#: django_authopenid/forms.py:134
-msgid "Account with this name already exists on the forum"
-msgstr ""
-
-#: django_authopenid/forms.py:135
-msgid "can't have two logins to the same account yet, sorry."
-msgstr ""
-
-#: django_authopenid/forms.py:157
-msgid "Please enter valid username and password (both are case-sensitive)."
-msgstr ""
-
-#: django_authopenid/forms.py:160 django_authopenid/forms.py:210
-msgid "This account is inactive."
-msgstr ""
-
-#: django_authopenid/forms.py:162
-msgid "Login failed."
-msgstr ""
-
-#: django_authopenid/forms.py:164
-msgid "Please enter username and password"
-msgstr ""
-
-#: django_authopenid/forms.py:166
-msgid "Please enter your password"
-msgstr ""
-
-#: django_authopenid/forms.py:168
-msgid "Please enter user name"
-msgstr ""
-
-#: django_authopenid/forms.py:206
-msgid ""
-"Please enter a valid username and password. Note that "
-"both fields are case-sensitive."
-msgstr ""
-
-#: django_authopenid/forms.py:229
-msgid "Current password"
-msgstr ""
-
-#: django_authopenid/forms.py:240
-msgid ""
-"Old password is incorrect. Please enter the correct "
-"password."
-msgstr ""
-
-#: django_authopenid/forms.py:305
-msgid "Your user name (<i>required</i>)"
-msgstr ""
-
-#: django_authopenid/forms.py:320
-msgid "Incorrect username."
-msgstr "sorry, there is no such user name"
-
-#: django_authopenid/urls.py:23 django_authopenid/urls.py:24
-#: django_authopenid/urls.py:25 django_authopenid/urls.py:27
-#: fbconnect/urls.py:12 fbconnect/urls.py:13 fbconnect/urls.py:14
-#: forum/urls.py:32
-msgid "signin/"
-msgstr ""
-
-#: django_authopenid/urls.py:24 fbconnect/urls.py:13 fbconnect/urls.py:17
-msgid "newquestion/"
-msgstr ""
-
-#: django_authopenid/urls.py:25 fbconnect/urls.py:14 fbconnect/urls.py:18
-msgid "newanswer/"
-msgstr ""
-
-#: django_authopenid/urls.py:26
-msgid "signout/"
-msgstr ""
-
-#: django_authopenid/urls.py:27
-msgid "complete/"
-msgstr ""
-
-#: django_authopenid/urls.py:29 fbconnect/urls.py:16 fbconnect/urls.py:17
-#: fbconnect/urls.py:18
-msgid "register/"
-msgstr ""
-
-#: django_authopenid/urls.py:30
-msgid "signup/"
-msgstr ""
-
-#: django_authopenid/urls.py:32
-msgid "sendpw/"
-msgstr ""
-
-#: django_authopenid/urls.py:33 django_authopenid/urls.py:37
-msgid "password/"
-msgstr ""
-
-#: django_authopenid/urls.py:33
-msgid "confirm/"
-msgstr ""
-
-#: django_authopenid/urls.py:36
-msgid "account_settings"
-msgstr ""
-
-#: django_authopenid/urls.py:38 django_authopenid/urls.py:39
-#: django_authopenid/urls.py:40 django_authopenid/urls.py:41
-msgid "email/"
-msgstr ""
-
-#: django_authopenid/urls.py:38
-msgid "validate/"
-msgstr ""
-
-#: django_authopenid/urls.py:39
-msgid "change/"
-msgstr ""
-
-#: django_authopenid/urls.py:40
-msgid "sendkey/"
-msgstr ""
-
-#: django_authopenid/urls.py:41
-msgid "verify/"
-msgstr ""
-
-#: django_authopenid/urls.py:42
-msgid "openid/"
-msgstr ""
-
-#: django_authopenid/urls.py:43 forum/urls.py:52 forum/urls.py:56
-msgid "delete/"
-msgstr ""
-
-#: django_authopenid/urls.py:51
-msgid "external-login/forgot-password/"
-msgstr ""
-
-#: django_authopenid/urls.py:54
-msgid "external-login/signup/"
-msgstr ""
-
-#: django_authopenid/views.py:125
-#, python-format
-msgid "OpenID %(openid_url)s is invalid"
-msgstr ""
-
-#: django_authopenid/views.py:593
-msgid "Welcome email subject line"
-msgstr "Welcome to the Q&A forum"
-
-#: django_authopenid/views.py:699
-msgid "Password changed."
-msgstr ""
-
-#: django_authopenid/views.py:711 django_authopenid/views.py:717
-#, python-format
-msgid "your email needs to be validated see %(details_url)s"
-msgstr ""
-"Your email needs to be validated. Please see details <a "
-"id='validate_email_alert' href='%(details_url)s'>here</a>."
-
-#: django_authopenid/views.py:738
-msgid "Email verification subject line"
-msgstr "Verification Email from Q&A forum"
-
-#: django_authopenid/views.py:829
-msgid "your email was not changed"
-msgstr ""
-
-#: django_authopenid/views.py:877 django_authopenid/views.py:1035
-#, python-format
-msgid "No OpenID %s found associated in our database"
-msgstr ""
-
-#: django_authopenid/views.py:881 django_authopenid/views.py:1042
-#, python-format
-msgid "The OpenID %s isn't associated to current user logged in"
-msgstr ""
-
-#: django_authopenid/views.py:889
-msgid "Email Changed."
-msgstr ""
-
-#: django_authopenid/views.py:967
-msgid "This OpenID is already associated with another account."
-msgstr ""
-
-#: django_authopenid/views.py:972
-#, python-format
-msgid "OpenID %s is now associated with your account."
-msgstr ""
-
-#: django_authopenid/views.py:1045
-msgid "Account deleted."
-msgstr ""
-
-#: django_authopenid/views.py:1097
-msgid "Request for new password"
-msgstr ""
-
-#: django_authopenid/views.py:1111
-msgid "A new password and the activation link were sent to your email address."
-msgstr ""
-
-#: django_authopenid/views.py:1143
-#, python-format
-msgid ""
-"Could not change password. Confirmation key '%s' is not "
-"registered."
-msgstr ""
-
-#: django_authopenid/views.py:1153
-msgid ""
-"Can not change password. User don't exist anymore in our "
-"database."
-msgstr ""
-
-#: django_authopenid/views.py:1163
-#, python-format
-msgid "Password changed for %s. You may now sign in."
-msgstr ""
-
#: forum/auth.py:484
msgid "Your question and all of it's answers have been deleted"
msgstr ""
@@ -410,58 +184,58 @@ msgstr ""
msgid "latest questions"
msgstr ""
-#: forum/forms.py:19 forum/skins/default/templates/answer_edit_tips.html:35
+#: forum/forms.py:22 forum/skins/default/templates/answer_edit_tips.html:35
#: forum/skins/default/templates/answer_edit_tips.html:39
#: forum/skins/default/templates/question_edit_tips.html:32
#: forum/skins/default/templates/question_edit_tips.html:37
msgid "title"
msgstr ""
-#: forum/forms.py:20
+#: forum/forms.py:23
msgid "please enter a descriptive title for your question"
msgstr ""
-#: forum/forms.py:25
+#: forum/forms.py:28
msgid "title must be > 10 characters"
msgstr ""
-#: forum/forms.py:34
+#: forum/forms.py:37
msgid "content"
msgstr ""
-#: forum/forms.py:40
+#: forum/forms.py:43
msgid "question content must be > 10 characters"
msgstr ""
-#: forum/forms.py:50 forum/skins/default/templates/header.html:28
+#: forum/forms.py:53 forum/skins/default/templates/header.html:28
#: forum/skins/default/templates/header.html:56
msgid "tags"
msgstr ""
-#: forum/forms.py:52
+#: forum/forms.py:55
msgid ""
"Tags are short keywords, with no spaces within. Up to five tags can be used."
msgstr ""
-#: forum/forms.py:59 forum/skins/default/templates/question_retag.html:39
+#: forum/forms.py:62 forum/skins/default/templates/question_retag.html:39
msgid "tags are required"
msgstr ""
-#: forum/forms.py:65
+#: forum/forms.py:68
msgid "please use 5 tags or less"
msgstr ""
-#: forum/forms.py:68
+#: forum/forms.py:71
msgid "tags must be shorter than 20 characters"
msgstr ""
-#: forum/forms.py:72
+#: forum/forms.py:75
msgid ""
"please use following characters in tags: letters 'a-z', numbers, and "
"characters '.-_#'"
msgstr ""
-#: forum/forms.py:82
+#: forum/forms.py:85
#: forum/skins/default/templates/post_contributor_info.html:7
#: forum/skins/default/templates/question_summary_list_roll.html:26
#: forum/skins/default/templates/question_summary_list_roll.html:38
@@ -470,119 +244,117 @@ msgstr ""
msgid "community wiki"
msgstr ""
-#: forum/forms.py:83
+#: forum/forms.py:86
msgid ""
"if you choose community wiki option, the question and answer do not generate "
"points and name of author will not be shown"
msgstr ""
-#: forum/forms.py:99
+#: forum/forms.py:102
msgid "update summary:"
msgstr ""
-#: forum/forms.py:100
+#: forum/forms.py:103
msgid ""
"enter a brief summary of your revision (e.g. fixed spelling, grammar, "
"improved style, this field is optional)"
msgstr ""
-#: forum/forms.py:103
+#: forum/forms.py:106
msgid "Automatically accept user's contributions for the email updates"
msgstr ""
-#: forum/forms.py:119
+#: forum/forms.py:122
msgid "Your name:"
msgstr ""
-#: forum/forms.py:120
+#: forum/forms.py:123
msgid "Email (not shared with anyone):"
msgstr ""
-#: forum/forms.py:121
+#: forum/forms.py:124
msgid "Your message:"
msgstr ""
-#: forum/forms.py:203
+#: forum/forms.py:206
msgid "this email does not have to be linked to gravatar"
msgstr ""
-#: forum/forms.py:205
+#: forum/forms.py:208
msgid "Screen name"
msgstr ""
-#: forum/forms.py:206
+#: forum/forms.py:209
msgid "Real name"
msgstr ""
-#: forum/forms.py:207
+#: forum/forms.py:210
msgid "Website"
msgstr ""
-#: forum/forms.py:208
+#: forum/forms.py:211
msgid "Location"
msgstr ""
-#: forum/forms.py:209
+#: forum/forms.py:212
msgid "Date of birth"
msgstr ""
-#: forum/forms.py:209
+#: forum/forms.py:212
msgid "will not be shown, used to calculate age, format: YYYY-MM-DD"
msgstr ""
-#: forum/forms.py:210
+#: forum/forms.py:213 forum/skins/default/templates/account_settings.html:21
#: forum/skins/default/templates/authopenid/settings.html:21
msgid "Profile"
msgstr ""
-#: forum/forms.py:241 forum/forms.py:242
+#: forum/forms.py:244 forum/forms.py:245
msgid "this email has already been registered, please use another one"
msgstr ""
-#: forum/forms.py:248
+#: forum/forms.py:251
msgid "Choose email tag filter"
msgstr ""
-#: forum/forms.py:263 forum/forms.py:264
+#: forum/forms.py:269
+msgid "Current password"
+msgstr ""
+
+#: forum/forms.py:280
+msgid ""
+"Old password is incorrect. Please enter the correct "
+"password."
+msgstr ""
+
+#: forum/forms.py:285 forum/forms.py:286
msgid "weekly"
msgstr ""
-#: forum/forms.py:263 forum/forms.py:264
+#: forum/forms.py:285 forum/forms.py:286
msgid "no email"
msgstr ""
-#: forum/forms.py:264
+#: forum/forms.py:286
msgid "daily"
msgstr ""
-#: forum/forms.py:279
+#: forum/forms.py:301
msgid "Asked by me"
msgstr ""
-#: forum/forms.py:282
+#: forum/forms.py:304
msgid "Answered by me"
msgstr ""
-#: forum/forms.py:285
+#: forum/forms.py:307
msgid "Individually selected"
msgstr ""
-#: forum/forms.py:288
+#: forum/forms.py:310
msgid "Entire forum (tag filtered)"
msgstr ""
-#: forum/forms.py:342
-msgid "okay, let's try!"
-msgstr ""
-
-#: forum/forms.py:343
-msgid "no OSQA community email please, thanks"
-msgstr ""
-
-#: forum/forms.py:346
-msgid "please choose one of the options above"
-msgstr ""
-
#: forum/urls.py:28
msgid "upfiles/"
msgstr ""
@@ -654,6 +426,10 @@ msgstr ""
msgid "command/"
msgstr ""
+#: forum/urls.py:52 forum/urls.py:56
+msgid "delete/"
+msgstr ""
+
#: forum/urls.py:60 forum/views/readers.py:435
msgid "question/"
msgstr ""
@@ -714,15 +490,49 @@ msgstr ""
msgid "feedback/"
msgstr ""
-#: forum/urls.py:89 forum/urls.py:90
+#: forum/urls.py:93 forum/urls.py:94 forum/urls.py:95 forum/urls.py:96
+#: forum/urls.py:97 forum/urls.py:98 forum/urls.py:100 forum/urls.py:102
+#: forum_modules/localauth/urls.py:7
msgid "account/"
msgstr ""
-#: forum/management/commands/send_email_alerts.py:155
+#: forum/urls.py:93 forum/urls.py:95 forum/urls.py:96
+msgid "signin/"
+msgstr ""
+
+#: forum/urls.py:94
+msgid "signout/"
+msgstr ""
+
+#: forum/urls.py:97
+msgid "done/"
+msgstr ""
+
+#: forum/urls.py:98 forum_modules/localauth/urls.py:7
+msgid "register/"
+msgstr ""
+
+#: forum/urls.py:100
+msgid "password/"
+msgstr ""
+
+#: forum/authentication/forms.py:17
+msgid "okay, let's try!"
+msgstr ""
+
+#: forum/authentication/forms.py:18
+msgid "no OSQA community email please, thanks"
+msgstr ""
+
+#: forum/authentication/forms.py:21
+msgid "please choose one of the options above"
+msgstr ""
+
+#: forum/management/commands/send_email_alerts.py:156
msgid "email update message subject"
msgstr "news from Q&A forum"
-#: forum/management/commands/send_email_alerts.py:157
+#: forum/management/commands/send_email_alerts.py:158
#, python-format
msgid "%(name)s, this is an update message header for a question"
msgid_plural "%(name)s, this is an update message header for %(num)d questions"
@@ -733,11 +543,11 @@ msgstr[1] ""
"<p>Dear %(name)s,</p><p>The following %(num)d questions have been updated on "
"the Q&A forum:</p>"
-#: forum/management/commands/send_email_alerts.py:168
+#: forum/management/commands/send_email_alerts.py:169
msgid "new question"
msgstr ""
-#: forum/management/commands/send_email_alerts.py:178
+#: forum/management/commands/send_email_alerts.py:179
#, python-format
msgid "There is also one question which was recently "
msgid_plural ""
@@ -745,12 +555,12 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: forum/management/commands/send_email_alerts.py:183
+#: forum/management/commands/send_email_alerts.py:184
msgid ""
"Perhaps you could look up previously sent forum reminders in your mailbox."
msgstr ""
-#: forum/management/commands/send_email_alerts.py:187
+#: forum/management/commands/send_email_alerts.py:188
#, python-format
msgid ""
"go to %(link)s to change frequency of email updates or %(email)s "
@@ -762,10 +572,10 @@ msgstr ""
"administrator at %(email)s.</p><p>Sincerely,</p><p>Your friendly Q&A forum "
"server.</p>"
-#: forum/middleware/anon_user.py:33
+#: forum/middleware/anon_user.py:34
#, python-format
-msgid "first time greeting with %(url)s"
-msgstr "Hello and welcome to OSQA - <a href='%(url)s'>please join us</a>!"
+msgid "First time here? Check out the <a href=\"%s\">FAQ</a>!"
+msgstr ""
#: forum/models/question.py:247
#, python-format
@@ -792,15 +602,15 @@ msgstr ""
msgid "%(people)s commented an answer"
msgstr ""
-#: forum/models/repute.py:11 forum/skins/default/templates/badges.html:53
+#: forum/models/repute.py:13 forum/skins/default/templates/badges.html:53
msgid "gold"
msgstr ""
-#: forum/models/repute.py:12 forum/skins/default/templates/badges.html:61
+#: forum/models/repute.py:14 forum/skins/default/templates/badges.html:61
msgid "silver"
msgstr ""
-#: forum/models/repute.py:13 forum/skins/default/templates/badges.html:68
+#: forum/models/repute.py:15 forum/skins/default/templates/badges.html:68
msgid "bronze"
msgstr ""
@@ -812,31 +622,31 @@ msgstr ""
msgid "ignored"
msgstr ""
-#: forum/models/user.py:31
+#: forum/models/user.py:33
msgid "Entire forum"
msgstr ""
-#: forum/models/user.py:32
+#: forum/models/user.py:34
msgid "Questions that I asked"
msgstr ""
-#: forum/models/user.py:33
+#: forum/models/user.py:35
msgid "Questions that I answered"
msgstr ""
-#: forum/models/user.py:34
+#: forum/models/user.py:36
msgid "Individually selected questions"
msgstr ""
-#: forum/models/user.py:37
+#: forum/models/user.py:39
msgid "Weekly"
msgstr ""
-#: forum/models/user.py:38
+#: forum/models/user.py:40
msgid "Daily"
msgstr ""
-#: forum/models/user.py:39
+#: forum/models/user.py:41
msgid "No email"
msgstr ""
@@ -908,6 +718,58 @@ msgstr ""
msgid "About"
msgstr ""
+#: forum/skins/default/templates/account_settings.html:4
+#: forum/skins/default/templates/authopenid/settings.html:4
+msgid "Account functions"
+msgstr ""
+
+#: forum/skins/default/templates/account_settings.html:29
+#: forum/skins/default/templates/changepw.html:5
+#: forum/skins/default/templates/changepw.html:14
+#: forum/skins/default/templates/authopenid/changepw.html:5
+#: forum/skins/default/templates/authopenid/changepw.html:14
+#: forum/skins/default/templates/authopenid/settings.html:29
+msgid "Change password"
+msgstr ""
+
+#: forum/skins/default/templates/account_settings.html:30
+#: forum/skins/default/templates/authopenid/settings.html:30
+msgid "Give your account a new password."
+msgstr ""
+
+#: forum/skins/default/templates/account_settings.html:32
+#: forum/skins/default/templates/authopenid/settings.html:31
+msgid "Change email "
+msgstr ""
+
+#: forum/skins/default/templates/account_settings.html:33
+#: forum/skins/default/templates/authopenid/settings.html:32
+msgid "Add or update the email address associated with your account."
+msgstr ""
+
+#: forum/skins/default/templates/account_settings.html:35
+#: forum/skins/default/templates/authopenid/changeopenid.html:4
+#: forum/skins/default/templates/authopenid/changeopenid.html:30
+#: forum/skins/default/templates/authopenid/settings.html:34
+msgid "Change OpenID"
+msgstr ""
+
+#: forum/skins/default/templates/account_settings.html:36
+#: forum/skins/default/templates/authopenid/settings.html:35
+msgid "Change openid associated to your account"
+msgstr ""
+
+#: forum/skins/default/templates/account_settings.html:39
+#: forum/skins/default/templates/authopenid/delete.html:4
+#: forum/skins/default/templates/authopenid/settings.html:38
+msgid "Delete account"
+msgstr ""
+
+#: forum/skins/default/templates/account_settings.html:40
+#: forum/skins/default/templates/authopenid/settings.html:39
+msgid "Erase your username and all your data from website"
+msgstr ""
+
#: forum/skins/default/templates/answer_edit.html:5
#: forum/skins/default/templates/answer_edit.html:48
msgid "Edit answer"
@@ -1270,6 +1132,18 @@ msgstr ""
msgid "subscribe to the questions feed"
msgstr ""
+#: forum/skins/default/templates/changepw.html:7
+#: forum/skins/default/templates/authopenid/changepw.html:7
+msgid "Account: change password"
+msgstr "Change your password"
+
+#: forum/skins/default/templates/changepw.html:8
+#: forum/skins/default/templates/authopenid/changepw.html:8
+msgid "This is where you can change your password. Make sure you remember it!"
+msgstr ""
+"<span class='strong'>To change your password</span> please fill out and "
+"submit this form"
+
#: forum/skins/default/templates/close.html:6
#: forum/skins/default/templates/close.html:16
msgid "Close question"
@@ -1423,11 +1297,11 @@ msgstr ""
msgid "delete any questions and answers and perform other moderation tasks"
msgstr ""
-#: forum/skins/default/templates/faq.html:102
+#: forum/skins/default/templates/faq.html:103
msgid "how to validate email title"
msgstr "How to validate email and why?"
-#: forum/skins/default/templates/faq.html:104
+#: forum/skins/default/templates/faq.html:105
#, python-format
msgid ""
"how to validate email info with %(send_email_key_url)s %(gravatar_faq_url)s"
@@ -1445,11 +1319,11 @@ msgstr ""
"Also, when you sign up for the first time - create a unique <a href='%"
"(gravatar_faq_url)s'><strong>gravatar</strong></a> personal image.</p>"
-#: forum/skins/default/templates/faq.html:108
+#: forum/skins/default/templates/faq.html:110
msgid "what is gravatar"
msgstr "What is gravatar?"
-#: forum/skins/default/templates/faq.html:109
+#: forum/skins/default/templates/faq.html:111
msgid "gravatar faq info"
msgstr ""
"<strong>Gravatar</strong> means <strong>g</strong>lobally <strong>r</"
@@ -1460,44 +1334,44 @@ msgstr ""
"your image</strong> at <a href='http://gravatar.com'><strong>gravatar.com</"
"strong></a>"
-#: forum/skins/default/templates/faq.html:112
+#: forum/skins/default/templates/faq.html:114
msgid "To register, do I need to create new password?"
msgstr ""
-#: forum/skins/default/templates/faq.html:113
+#: forum/skins/default/templates/faq.html:115
msgid ""
"No, you don't have to. You can login through any service that supports "
"OpenID, e.g. Google, Yahoo, AOL, etc."
msgstr ""
-#: forum/skins/default/templates/faq.html:114
+#: forum/skins/default/templates/faq.html:116
msgid "Login now!"
msgstr ""
-#: forum/skins/default/templates/faq.html:119
+#: forum/skins/default/templates/faq.html:121
msgid "Why other people can edit my questions/answers?"
msgstr ""
-#: forum/skins/default/templates/faq.html:120
+#: forum/skins/default/templates/faq.html:122
msgid "Goal of this site is..."
msgstr ""
-#: forum/skins/default/templates/faq.html:120
+#: forum/skins/default/templates/faq.html:122
msgid ""
"So questions and answers can be edited like wiki pages by experienced users "
"of this site and this improves the overall quality of the knowledge base "
"content."
msgstr ""
-#: forum/skins/default/templates/faq.html:121
+#: forum/skins/default/templates/faq.html:123
msgid "If this approach is not for you, we respect your choice."
msgstr ""
-#: forum/skins/default/templates/faq.html:125
+#: forum/skins/default/templates/faq.html:127
msgid "Still have questions?"
msgstr ""
-#: forum/skins/default/templates/faq.html:126
+#: forum/skins/default/templates/faq.html:128
#, python-format
msgid ""
"Please ask your question at %(ask_question_url)s, help make our community "
@@ -1506,13 +1380,13 @@ msgstr ""
"Please <a href='%(ask_question_url)s'>ask</a> your question, help make our "
"community better!"
-#: forum/skins/default/templates/faq.html:128
+#: forum/skins/default/templates/faq.html:130
#: forum/skins/default/templates/header.html:27
#: forum/skins/default/templates/header.html:55
msgid "questions"
msgstr ""
-#: forum/skins/default/templates/faq.html:128
+#: forum/skins/default/templates/faq.html:130
#: forum/skins/default/templates/index.html:121
#: forum/skins/default/templates/index_.html:121
msgid "."
@@ -1768,6 +1642,7 @@ msgstr "list of all questions"
#: forum/skins/default/templates/index.html:121
#: forum/skins/default/templates/index_.html:121
+#: forum/skins/default/templates/auth/signup.html:28
#: forum/skins/default/templates/authopenid/signup.html:28
msgid "or"
msgstr ""
@@ -2588,7 +2463,7 @@ msgid "avatar, see %(gravatar_faq_url)s"
msgstr "<a href='%(gravatar_faq_url)s'>gravatar</a>"
#: forum/skins/default/templates/user_edit.html:36
-#: forum/skins/default/templates/user_info.html:56
+#: forum/skins/default/templates/user_info.html:60
msgid "Registered user"
msgstr ""
@@ -2619,7 +2494,7 @@ msgid "Stop sending email"
msgstr "Stop Email"
#: forum/skins/default/templates/user_info.html:22
-#: forum/skins/default/templates/users.html:26 forum/views/users.py:907
+#: forum/skins/default/templates/users.html:26 forum/views/users.py:916
msgid "reputation"
msgstr "karma"
@@ -2631,39 +2506,39 @@ msgstr ""
msgid "update profile"
msgstr ""
-#: forum/skins/default/templates/user_info.html:60
+#: forum/skins/default/templates/user_info.html:64
msgid "real name"
msgstr ""
-#: forum/skins/default/templates/user_info.html:65
+#: forum/skins/default/templates/user_info.html:69
msgid "member for"
msgstr "member since"
-#: forum/skins/default/templates/user_info.html:70
+#: forum/skins/default/templates/user_info.html:74
msgid "last seen"
msgstr ""
-#: forum/skins/default/templates/user_info.html:76
+#: forum/skins/default/templates/user_info.html:80
msgid "user website"
msgstr ""
-#: forum/skins/default/templates/user_info.html:82
+#: forum/skins/default/templates/user_info.html:86
msgid "location"
msgstr ""
-#: forum/skins/default/templates/user_info.html:89
+#: forum/skins/default/templates/user_info.html:93
msgid "age"
msgstr ""
-#: forum/skins/default/templates/user_info.html:90
+#: forum/skins/default/templates/user_info.html:94
msgid "age unit"
msgstr "years old"
-#: forum/skins/default/templates/user_info.html:96
+#: forum/skins/default/templates/user_info.html:100
msgid "todays unused votes"
msgstr ""
-#: forum/skins/default/templates/user_info.html:97
+#: forum/skins/default/templates/user_info.html:101
msgid "votes left"
msgstr ""
@@ -2784,19 +2659,19 @@ msgstr[1] ""
msgid "User profile"
msgstr ""
-#: forum/skins/default/templates/user_tabs.html:7 forum/views/users.py:881
+#: forum/skins/default/templates/user_tabs.html:7 forum/views/users.py:890
msgid "overview"
msgstr ""
-#: forum/skins/default/templates/user_tabs.html:9 forum/views/users.py:889
+#: forum/skins/default/templates/user_tabs.html:9 forum/views/users.py:898
msgid "recent activity"
msgstr ""
-#: forum/skins/default/templates/user_tabs.html:12 forum/views/users.py:899
+#: forum/skins/default/templates/user_tabs.html:12 forum/views/users.py:908
msgid "comments and answers to others questions"
msgstr ""
-#: forum/skins/default/templates/user_tabs.html:13 forum/views/users.py:898
+#: forum/skins/default/templates/user_tabs.html:13 forum/views/users.py:907
msgid "responses"
msgstr ""
@@ -2808,11 +2683,11 @@ msgstr "Graph of user karma"
msgid "reputation history"
msgstr "karma history"
-#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:925
+#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:934
msgid "user vote record"
msgstr ""
-#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:924
+#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:933
msgid "casted votes"
msgstr "votes"
@@ -2824,11 +2699,11 @@ msgstr ""
msgid "favorites"
msgstr ""
-#: forum/skins/default/templates/user_tabs.html:27 forum/views/users.py:934
+#: forum/skins/default/templates/user_tabs.html:27 forum/views/users.py:943
msgid "email subscription settings"
msgstr ""
-#: forum/skins/default/templates/user_tabs.html:28 forum/views/users.py:933
+#: forum/skins/default/templates/user_tabs.html:28 forum/views/users.py:942
msgid "email subscriptions"
msgstr ""
@@ -2870,6 +2745,204 @@ msgstr ""
msgid "thumb-up off"
msgstr ""
+#: forum/skins/default/templates/auth/complete.html:5
+#: forum/skins/default/templates/authopenid/complete.html:19
+msgid "Connect your OpenID with this site"
+msgstr "New user signup"
+
+#: forum/skins/default/templates/auth/complete.html:8
+#: forum/skins/default/templates/authopenid/complete.html:22
+msgid "Connect your OpenID with your account on this site"
+msgstr "New user signup"
+
+#: forum/skins/default/templates/auth/complete.html:12
+msgid "You are here for the first time with "
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:13
+msgid ""
+"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. "
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:15
+#: forum/skins/default/templates/authopenid/complete.html:42
+msgid "This account already exists, please use another."
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:30
+#: forum/skins/default/templates/authopenid/complete.html:57
+msgid "Sorry, looks like we have some errors:"
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:47
+#: forum/skins/default/templates/authopenid/complete.html:82
+msgid "Screen name label"
+msgstr "<strong>Screen Name</strong> (<i>will be shown to others</i>)"
+
+#: forum/skins/default/templates/auth/complete.html:54
+#: forum/skins/default/templates/authopenid/complete.html:89
+msgid "Email address label"
+msgstr ""
+"<strong>Email Address</strong> (<i>will <strong>not</strong> be shared with "
+"anyone, must be valid</i>)"
+
+#: forum/skins/default/templates/auth/complete.html:60
+#: forum/skins/default/templates/auth/signup.html:18
+#: forum/skins/default/templates/authopenid/complete.html:95
+#: forum/skins/default/templates/authopenid/signup.html:18
+msgid "receive updates motivational blurb"
+msgstr ""
+"<strong>Receive forum updates by email</strong> - this will help our "
+"community grow and become more useful.<br/>By default <span "
+"class='orange'>Q&amp;A</span> forum sends up to <strong>one email digest per "
+"week</strong> - only when there is anything new.<br/>If you like, please "
+"adjust this now or any time later from your user account."
+
+#: forum/skins/default/templates/auth/complete.html:64
+#: forum/skins/default/templates/auth/signup.html:22
+#: forum/skins/default/templates/authopenid/complete.html:99
+#: forum/skins/default/templates/authopenid/signup.html:22
+msgid "please select one of the options above"
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:67
+#: forum/skins/default/templates/authopenid/complete.html:102
+msgid "Tag filter tool will be your right panel, once you log in."
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:68
+#: forum/skins/default/templates/authopenid/complete.html:103
+msgid "create account"
+msgstr "Signup"
+
+#: forum/skins/default/templates/auth/complete.html:77
+#: forum/skins/default/templates/authopenid/complete.html:112
+msgid "Existing account"
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:78
+#: forum/skins/default/templates/authopenid/complete.html:113
+msgid "user name"
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:79
+#: forum/skins/default/templates/authopenid/complete.html:114
+msgid "password"
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:86
+#: forum/skins/default/templates/authopenid/complete.html:121
+msgid "Register"
+msgstr ""
+
+#: forum/skins/default/templates/auth/complete.html:87
+#: forum/skins/default/templates/authopenid/complete.html:122
+#: forum/skins/default/templates/authopenid/signin.html:151
+msgid "Forgot your password?"
+msgstr ""
+
+#: forum/skins/default/templates/auth/signin.html:22
+#: forum/skins/default/templates/authopenid/signin.html:5
+#: forum/skins/default/templates/authopenid/signin.html:21
+msgid "User login"
+msgstr "User login"
+
+#: forum/skins/default/templates/auth/signin.html:29
+#: forum/skins/default/templates/authopenid/signin.html:42
+msgid "Click to sign in through any of these services."
+msgstr ""
+"<p><span class=\"big strong\">Please select your favorite login method below."
+"</span></p><p><font color=\"gray\">External login services use <a href="
+"\"http://openid.net\"><b>OpenID</b></a> technology, where your password "
+"always stays confidential between you and your login provider and you don't "
+"have to remember another one. CNPROG option requires your login name and "
+"password entered here.</font></p>"
+
+#: forum/skins/default/templates/auth/signin.html:34
+msgid ""
+"Take the opurtunity to validate my email next to the external provider I "
+"choose."
+msgstr ""
+
+#: forum/skins/default/templates/auth/signin.html:84
+msgid "Enter your "
+msgstr ""
+
+#: forum/skins/default/templates/auth/signin.html:140
+#: forum/skins/default/templates/authopenid/signin.html:160
+msgid "Why use OpenID?"
+msgstr ""
+
+#: forum/skins/default/templates/auth/signin.html:143
+#: forum/skins/default/templates/authopenid/signin.html:163
+msgid "with openid it is easier"
+msgstr "With the OpenID you don't need to create new username and password."
+
+#: forum/skins/default/templates/auth/signin.html:146
+#: forum/skins/default/templates/authopenid/signin.html:166
+msgid "reuse openid"
+msgstr "You can safely re-use the same login for all OpenID-enabled websites."
+
+#: forum/skins/default/templates/auth/signin.html:149
+#: forum/skins/default/templates/authopenid/signin.html:169
+msgid "openid is widely adopted"
+msgstr ""
+"There are > 160,000,000 OpenID account in use. Over 10,000 sites are OpenID-"
+"enabled."
+
+#: forum/skins/default/templates/auth/signin.html:152
+#: forum/skins/default/templates/authopenid/signin.html:172
+msgid "openid is supported open standard"
+msgstr "OpenID is based on an open standard, supported by many organizations."
+
+#: forum/skins/default/templates/auth/signin.html:157
+#: forum/skins/default/templates/authopenid/signin.html:177
+msgid "Find out more"
+msgstr ""
+
+#: forum/skins/default/templates/auth/signin.html:158
+#: forum/skins/default/templates/authopenid/signin.html:178
+msgid "Get OpenID"
+msgstr ""
+
+#: forum/skins/default/templates/auth/signup.html:4
+#: forum/skins/default/templates/authopenid/signup.html:4
+msgid "Signup"
+msgstr ""
+
+#: forum/skins/default/templates/auth/signup.html:8
+#: forum/skins/default/templates/authopenid/signup.html:8
+msgid "Create login name and password"
+msgstr ""
+
+#: forum/skins/default/templates/auth/signup.html:10
+#: forum/skins/default/templates/authopenid/signup.html:10
+msgid "Traditional signup info"
+msgstr ""
+"<span class='strong big'>If you prefer, create your forum login name and "
+"password here. However</span>, please keep in mind that we also support "
+"<strong>OpenID</strong> login method. With <strong>OpenID</strong> you can "
+"simply reuse your external login (e.g. Gmail or AOL) without ever sharing "
+"your login details with anyone and having to remember yet another password."
+
+#: forum/skins/default/templates/auth/signup.html:25
+#: forum/skins/default/templates/authopenid/signup.html:25
+msgid ""
+"Please read and type in the two words below to help us prevent automated "
+"account creation."
+msgstr ""
+
+#: forum/skins/default/templates/auth/signup.html:27
+#: forum/skins/default/templates/authopenid/signup.html:27
+msgid "Create Account"
+msgstr ""
+
+#: forum/skins/default/templates/auth/signup.html:29
+msgid "return to login page"
+msgstr ""
+
#: forum/skins/default/templates/authopenid/changeemail.html:3
#: forum/skins/default/templates/authopenid/changeemail.html:9
#: forum/skins/default/templates/authopenid/changeemail.html:38
@@ -2980,12 +3053,6 @@ msgstr ""
"validated before</span> so the new key was not sent. You can <a href='%"
"(change_link)s'>change</a> email used for update subscriptions if necessary."
-#: forum/skins/default/templates/authopenid/changeopenid.html:4
-#: forum/skins/default/templates/authopenid/changeopenid.html:30
-#: forum/skins/default/templates/authopenid/settings.html:34
-msgid "Change OpenID"
-msgstr ""
-
#: forum/skins/default/templates/authopenid/changeopenid.html:8
msgid "Account: change OpenID URL"
msgstr ""
@@ -3005,30 +3072,6 @@ msgstr ""
msgid "OpenID URL:"
msgstr ""
-#: forum/skins/default/templates/authopenid/changepw.html:5
-#: forum/skins/default/templates/authopenid/changepw.html:14
-#: forum/skins/default/templates/authopenid/settings.html:29
-msgid "Change password"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/changepw.html:7
-msgid "Account: change password"
-msgstr "Change your password"
-
-#: forum/skins/default/templates/authopenid/changepw.html:8
-msgid "This is where you can change your password. Make sure you remember it!"
-msgstr ""
-"<span class='strong'>To change your password</span> please fill out and "
-"submit this form"
-
-#: forum/skins/default/templates/authopenid/complete.html:19
-msgid "Connect your OpenID with this site"
-msgstr "New user signup"
-
-#: forum/skins/default/templates/authopenid/complete.html:22
-msgid "Connect your OpenID with your account on this site"
-msgstr "New user signup"
-
#: forum/skins/default/templates/authopenid/complete.html:27
#, python-format
msgid "register new %(provider)s account info, see %(gravatar_faq_url)s"
@@ -3082,68 +3125,6 @@ msgstr ""
"and will be used to create and retrieve your unique avatar image - <a href='%"
"(gravatar_faq_url)s'><strong>gravatar</strong></a>.</p>"
-#: forum/skins/default/templates/authopenid/complete.html:42
-msgid "This account already exists, please use another."
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/complete.html:57
-msgid "Sorry, looks like we have some errors:"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/complete.html:82
-msgid "Screen name label"
-msgstr "<strong>Screen Name</strong> (<i>will be shown to others</i>)"
-
-#: forum/skins/default/templates/authopenid/complete.html:89
-msgid "Email address label"
-msgstr ""
-"<strong>Email Address</strong> (<i>will <strong>not</strong> be shared with "
-"anyone, must be valid</i>)"
-
-#: forum/skins/default/templates/authopenid/complete.html:95
-#: forum/skins/default/templates/authopenid/signup.html:18
-msgid "receive updates motivational blurb"
-msgstr ""
-"<strong>Receive forum updates by email</strong> - this will help our "
-"community grow and become more useful.<br/>By default <span "
-"class='orange'>Q&amp;A</span> forum sends up to <strong>one email digest per "
-"week</strong> - only when there is anything new.<br/>If you like, please "
-"adjust this now or any time later from your user account."
-
-#: forum/skins/default/templates/authopenid/complete.html:99
-#: forum/skins/default/templates/authopenid/signup.html:22
-msgid "please select one of the options above"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/complete.html:102
-msgid "Tag filter tool will be your right panel, once you log in."
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/complete.html:103
-msgid "create account"
-msgstr "Signup"
-
-#: forum/skins/default/templates/authopenid/complete.html:112
-msgid "Existing account"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/complete.html:113
-msgid "user name"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/complete.html:114
-msgid "password"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/complete.html:121
-msgid "Register"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/complete.html:122
-#: forum/skins/default/templates/authopenid/signin.html:151
-msgid "Forgot your password?"
-msgstr ""
-
#: forum/skins/default/templates/authopenid/confirm_email.txt:2
msgid "Thank you for registering at our Q&A forum!"
msgstr ""
@@ -3175,11 +3156,6 @@ msgstr ""
"Sincerely,\n"
"Q&A Forum Administrator"
-#: forum/skins/default/templates/authopenid/delete.html:4
-#: forum/skins/default/templates/authopenid/settings.html:38
-msgid "Delete account"
-msgstr ""
-
#: forum/skins/default/templates/authopenid/delete.html:8
msgid "Account: delete account"
msgstr ""
@@ -3282,35 +3258,6 @@ msgstr ""
"* login with user name %(username)s and password %(password)s\n"
"* go to your user profile and set the password to something you can remember"
-#: forum/skins/default/templates/authopenid/settings.html:4
-msgid "Account functions"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/settings.html:30
-msgid "Give your account a new password."
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/settings.html:31
-msgid "Change email "
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/settings.html:32
-msgid "Add or update the email address associated with your account."
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/settings.html:35
-msgid "Change openid associated to your account"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/settings.html:39
-msgid "Erase your username and all your data from website"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/signin.html:5
-#: forum/skins/default/templates/authopenid/signin.html:21
-msgid "User login"
-msgstr "User login"
-
#: forum/skins/default/templates/authopenid/signin.html:28
#, python-format
msgid ""
@@ -3335,16 +3282,6 @@ msgstr ""
"strong> %(summary)s...\"</i> <span class=\"strong big\">is saved and will be "
"posted once you log in.</span>"
-#: forum/skins/default/templates/authopenid/signin.html:42
-msgid "Click to sign in through any of these services."
-msgstr ""
-"<p><span class=\"big strong\">Please select your favorite login method below."
-"</span></p><p><font color=\"gray\">External login services use <a href="
-"\"http://openid.net\"><b>OpenID</b></a> technology, where your password "
-"always stays confidential between you and your login provider and you don't "
-"have to remember another one. CNPROG option requires your login name and "
-"password entered here.</font></p>"
-
#: forum/skins/default/templates/authopenid/signin.html:128
msgid "Enter your <span id=\"enter_your_what\">Provider user name</span>"
msgstr ""
@@ -3384,63 +3321,6 @@ msgstr ""
msgid "Create account"
msgstr ""
-#: forum/skins/default/templates/authopenid/signin.html:160
-msgid "Why use OpenID?"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/signin.html:163
-msgid "with openid it is easier"
-msgstr "With the OpenID you don't need to create new username and password."
-
-#: forum/skins/default/templates/authopenid/signin.html:166
-msgid "reuse openid"
-msgstr "You can safely re-use the same login for all OpenID-enabled websites."
-
-#: forum/skins/default/templates/authopenid/signin.html:169
-msgid "openid is widely adopted"
-msgstr ""
-"There are > 160,000,000 OpenID account in use. Over 10,000 sites are OpenID-"
-"enabled."
-
-#: forum/skins/default/templates/authopenid/signin.html:172
-msgid "openid is supported open standard"
-msgstr "OpenID is based on an open standard, supported by many organizations."
-
-#: forum/skins/default/templates/authopenid/signin.html:177
-msgid "Find out more"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/signin.html:178
-msgid "Get OpenID"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/signup.html:4
-msgid "Signup"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/signup.html:8
-msgid "Create login name and password"
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/signup.html:10
-msgid "Traditional signup info"
-msgstr ""
-"<span class='strong big'>If you prefer, create your forum login name and "
-"password here. However</span>, please keep in mind that we also support "
-"<strong>OpenID</strong> login method. With <strong>OpenID</strong> you can "
-"simply reuse your external login (e.g. Gmail or AOL) without ever sharing "
-"your login details with anyone and having to remember yet another password."
-
-#: forum/skins/default/templates/authopenid/signup.html:25
-msgid ""
-"Please read and type in the two words below to help us prevent automated "
-"account creation."
-msgstr ""
-
-#: forum/skins/default/templates/authopenid/signup.html:27
-msgid "Create Account"
-msgstr ""
-
#: forum/skins/default/templates/authopenid/signup.html:29
msgid "return to OpenID login"
msgstr ""
@@ -3544,6 +3424,24 @@ msgstr ""
msgid "sorry, entered passwords did not match, please try again"
msgstr ""
+#: forum/views/auth.py:94 forum/views/auth.py:103
+msgid ""
+"Sorry, these login credentials belong to anoother user. Plese terminate your "
+"current session and try again."
+msgstr ""
+
+#: forum/views/auth.py:96
+msgid "You are already logged in with that user."
+msgstr ""
+
+#: forum/views/auth.py:101
+msgid "These login credentials are already associated with your account."
+msgstr ""
+
+#: forum/views/auth.py:107
+msgid "These new credentials are now associated with your account."
+msgstr ""
+
#: forum/views/commands.py:217
#, python-format
msgid "subscription saved, %(email)s needs validation, see %(details_url)s"
@@ -3567,62 +3465,66 @@ msgstr ""
msgid "We look forward to hearing your feedback! Please, give it next time :)"
msgstr ""
-#: forum/views/users.py:842 forum/views/users.py:846
+#: forum/views/users.py:851 forum/views/users.py:855
msgid "changes saved"
msgstr ""
-#: forum/views/users.py:852
+#: forum/views/users.py:861
msgid "email updates canceled"
msgstr ""
-#: forum/views/users.py:882
+#: forum/views/users.py:891
msgid "user profile"
msgstr ""
-#: forum/views/users.py:883
+#: forum/views/users.py:892
msgid "user profile overview"
msgstr ""
-#: forum/views/users.py:890
+#: forum/views/users.py:899
msgid "recent user activity"
msgstr ""
-#: forum/views/users.py:891
+#: forum/views/users.py:900
msgid "profile - recent activity"
msgstr ""
-#: forum/views/users.py:900
+#: forum/views/users.py:909
msgid "profile - responses"
msgstr ""
-#: forum/views/users.py:908
+#: forum/views/users.py:917
msgid "user reputation in the community"
msgstr "user karma"
-#: forum/views/users.py:909
+#: forum/views/users.py:918
msgid "profile - user reputation"
msgstr "Profile - User's Karma"
-#: forum/views/users.py:915
+#: forum/views/users.py:924
msgid "favorite questions"
msgstr ""
-#: forum/views/users.py:916
+#: forum/views/users.py:925
msgid "users favorite questions"
msgstr ""
-#: forum/views/users.py:917
+#: forum/views/users.py:926
msgid "profile - favorite questions"
msgstr ""
-#: forum/views/users.py:926
+#: forum/views/users.py:935
msgid "profile - votes"
msgstr ""
-#: forum/views/users.py:935
+#: forum/views/users.py:944
msgid "profile - email subscriptions"
msgstr ""
+#: forum/views/users.py:977
+msgid "Password changed."
+msgstr ""
+
#: forum/views/writers.py:74
msgid "uploading images is limited to users with >60 reputation points"
msgstr "sorry, file uploading requires karma >60"
@@ -3647,6 +3549,98 @@ msgstr ""
msgid "books/"
msgstr ""
+#: forum_modules/facebookauth/authentication.py:29
+msgid "Sorry, your Facebook session has expired, please try again"
+msgstr ""
+
+#: forum_modules/facebookauth/authentication.py:31
+msgid ""
+"The authentication with Facebook connect failed due to an invalid signature"
+msgstr ""
+
+#: forum_modules/facebookauth/authentication.py:33
+msgid ""
+"The authentication with Facebook connect failed, cannot find authentication "
+"tokens"
+msgstr ""
+
+#: forum_modules/localauth/forms.py:59
+msgid "Please enter valid username and password (both are case-sensitive)."
+msgstr ""
+
+#: forum_modules/localauth/forms.py:62
+msgid "This account is inactive."
+msgstr ""
+
+#: forum_modules/localauth/forms.py:64
+msgid "Login failed."
+msgstr ""
+
+#: forum_modules/localauth/forms.py:66
+msgid "Please enter username and password"
+msgstr ""
+
+#: forum_modules/localauth/forms.py:68
+msgid "Please enter your password"
+msgstr ""
+
+#: forum_modules/localauth/forms.py:70
+msgid "Please enter user name"
+msgstr ""
+
+#: forum_modules/localauth/urls.py:7
+msgid "local/"
+msgstr ""
+
+#: forum_modules/oauthauth/consumer.py:33
+msgid "Error, the oauth token is not on the server"
+msgstr ""
+
+#: forum_modules/oauthauth/consumer.py:38
+msgid "Something went wrong! Auth tokens do not match"
+msgstr ""
+
+#: forum_modules/openidauth/consumer.py:40
+msgid "Sorry, but your input is not a valid OpenId"
+msgstr ""
+
+#: forum_modules/openidauth/consumer.py:92
+msgid "The OpenId authentication request was canceled"
+msgstr ""
+
+#: forum_modules/openidauth/consumer.py:94
+msgid "The OpenId authentication failed: "
+msgstr ""
+
+#: forum_modules/openidauth/consumer.py:96
+msgid "Setup needed"
+msgstr ""
+
+#: forum_modules/openidauth/consumer.py:98
+msgid "The OpenId authentication failed with an unknown status: "
+msgstr ""
+
+#: forum_modules/openidauth/templates/openidurl.html:7
+msgid "Enter your OpenId Url"
+msgstr ""
+
+#~ msgid "Incorrect username."
+#~ msgstr "sorry, there is no such user name"
+
+#~ msgid "Welcome email subject line"
+#~ msgstr "Welcome to the Q&A forum"
+
+#~ msgid "your email needs to be validated see %(details_url)s"
+#~ msgstr ""
+#~ "Your email needs to be validated. Please see details <a "
+#~ "id='validate_email_alert' href='%(details_url)s'>here</a>."
+
+#~ msgid "Email verification subject line"
+#~ msgstr "Verification Email from Q&A forum"
+
+#~ msgid "first time greeting with %(url)s"
+#~ msgstr "Hello and welcome to OSQA - <a href='%(url)s'>please join us</a>!"
+
#~ msgid "have %(num_q)s unanswered questions"
#~ msgstr ""
#~ "<div class=\"questions-count\">%(num_q)s</div>questions <strong>without "
diff --git a/settings.py b/settings.py
index bbbd2f5b..9d231b8c 100755..100644
--- a/settings.py
+++ b/settings.py
@@ -11,6 +11,7 @@ SECRET_KEY = '$oo^&_m&qwbib=(_4m_n*zn-d=g#s0he5fx9xonnym#8p6yigm'
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
+ 'forum.modules.module_templates_loader',
'forum.skins.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
@@ -68,7 +69,6 @@ INSTALLED_APPS = [
'django.contrib.humanize',
'django.contrib.sitemaps',
'forum',
- 'django_authopenid',
'debug_toolbar' ,
]
@@ -77,9 +77,6 @@ AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend',]
if USE_SPHINX_SEARCH:
INSTALLED_APPS.append('djangosphinx')
-if USE_FB_CONNECT:
- INSTALLED_APPS.append('fbconnect')
-
if DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ) and False:#todo - is this always false?
USE_PG_FTS = True
INSTALLED_APPS.append('pgfulltext')