summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rwxr-xr-x.idea/compiler.xml24
-rwxr-xr-x.idea/copyright/profiles_settings.xml5
-rwxr-xr-x.idea/encodings.xml5
-rwxr-xr-x.idea/misc.xml127
-rwxr-xr-x.idea/modules.xml9
-rwxr-xr-x.idea/uiDesigner.xml125
-rwxr-xr-x.idea/vcs.xml7
-rwxr-xr-x.idea/workspace.xml687
-rw-r--r--INSTALL153
-rw-r--r--PENDING31
-rw-r--r--WISH_LIST (renamed from TODO)12
-rw-r--r--context.py1
-rw-r--r--[-rwxr-xr-x]cron/send_email_alerts14
-rw-r--r--cron/send_email_alerts_virtualenv10
-rw-r--r--django_authopenid/forms.py4
-rw-r--r--django_authopenid/util.py2
-rwxr-xr-xdjango_authopenid/views.py2
-rwxr-xr-xfbconnect/fb.py9
-rwxr-xr-xfbconnect/urls.py5
-rwxr-xr-xfbconnect/views.py33
-rw-r--r--forum/__init__.py1
-rw-r--r--forum/admin.py20
-rw-r--r--forum/auth.py2
-rw-r--r--forum/forms.py3
-rw-r--r--forum/forms.py.orig352
-rw-r--r--forum/management/__init__.py3
-rw-r--r--forum/management/commands/send_email_alerts.py3
-rw-r--r--forum/managers.py241
-rw-r--r--forum/middleware/__init__.py (renamed from middleware/__init__.py)0
-rw-r--r--forum/middleware/anon_user.py (renamed from middleware/anon_user.py)4
-rw-r--r--forum/middleware/cancel.py (renamed from middleware/cancel.py)2
-rw-r--r--forum/middleware/pagesize.py (renamed from middleware/pagesize.py)0
-rw-r--r--forum/models.py964
-rwxr-xr-xforum/models/__init__.py341
-rwxr-xr-xforum/models/answer.py133
-rwxr-xr-xforum/models/base.py139
-rwxr-xr-xforum/models/meta.py89
-rwxr-xr-xforum/models/question.py335
-rwxr-xr-xforum/models/repute.py109
-rwxr-xr-xforum/models/tag.py85
-rwxr-xr-xforum/models/user.py67
-rwxr-xr-xforum/modules.py54
-rw-r--r--forum/skins/README22
-rw-r--r--forum/skins/__init__.py57
-rw-r--r--forum/skins/common/media/README1
-rw-r--r--forum/skins/default/media/images/blue-up-arrow-h18px.png (renamed from templates/content/images/blue-up-arrow-h18px.png)bin593 -> 593 bytes
-rw-r--r--forum/skins/default/media/images/box-arrow.gif (renamed from templates/content/images/box-arrow.gif)bin69 -> 69 bytes
-rw-r--r--forum/skins/default/media/images/bullet_green.gif (renamed from templates/content/images/bullet_green.gif)bin64 -> 64 bytes
-rw-r--r--forum/skins/default/media/images/cc-88x31.png (renamed from templates/content/images/cc-88x31.png)bin5460 -> 5460 bytes
-rw-r--r--forum/skins/default/media/images/cc-wiki.png (renamed from templates/content/images/cc-wiki.png)bin2333 -> 2333 bytes
-rw-r--r--forum/skins/default/media/images/close-small-dark.png (renamed from templates/content/images/close-small-dark.png)bin226 -> 226 bytes
-rw-r--r--forum/skins/default/media/images/close-small-hover.png (renamed from templates/content/images/close-small-hover.png)bin337 -> 337 bytes
-rw-r--r--forum/skins/default/media/images/close-small.png (renamed from templates/content/images/close-small.png)bin293 -> 293 bytes
-rw-r--r--forum/skins/default/media/images/dash.gif (renamed from templates/content/images/dash.gif)bin44 -> 44 bytes
-rw-r--r--forum/skins/default/media/images/djangomade124x25_grey.gif (renamed from templates/content/images/djangomade124x25_grey.gif)bin2035 -> 2035 bytes
-rw-r--r--forum/skins/default/media/images/dot-g.gif (renamed from templates/content/images/dot-g.gif)bin61 -> 61 bytes
-rw-r--r--forum/skins/default/media/images/dot-list.gif (renamed from templates/content/images/dot-list.gif)bin56 -> 56 bytes
-rw-r--r--forum/skins/default/media/images/edit.png (renamed from templates/content/images/edit.png)bin758 -> 758 bytes
-rw-r--r--forum/skins/default/media/images/expander-arrow-hide.gif (renamed from templates/content/images/expander-arrow-hide.gif)bin126 -> 126 bytes
-rw-r--r--forum/skins/default/media/images/expander-arrow-show.gif (renamed from templates/content/images/expander-arrow-show.gif)bin135 -> 135 bytes
-rw-r--r--forum/skins/default/media/images/favicon.gif (renamed from templates/content/images/favicon.gif)bin3918 -> 3918 bytes
-rw-r--r--forum/skins/default/media/images/feed-icon-small.png (renamed from templates/content/images/feed-icon-small.png)bin689 -> 689 bytes
-rw-r--r--forum/skins/default/media/images/gray-up-arrow-h18px.png (renamed from templates/content/images/gray-up-arrow-h18px.png)bin383 -> 383 bytes
-rw-r--r--forum/skins/default/media/images/grippie.png (renamed from templates/content/images/grippie.png)bin162 -> 162 bytes
-rw-r--r--forum/skins/default/media/images/indicator.gif (renamed from templates/content/images/indicator.gif)bin2545 -> 2545 bytes
-rw-r--r--forum/skins/default/media/images/logo.gif (renamed from templates/content/images/logo.gif)bin2114 -> 2114 bytes
-rw-r--r--forum/skins/default/media/images/logo.png (renamed from templates/content/images/logo.png)bin2081 -> 2081 bytes
-rw-r--r--forum/skins/default/media/images/logo1.png (renamed from templates/content/images/logo1.png)bin2752 -> 2752 bytes
-rw-r--r--forum/skins/default/media/images/logo2.png (renamed from templates/content/images/logo2.png)bin2124 -> 2124 bytes
-rw-r--r--forum/skins/default/media/images/medala.gif (renamed from templates/content/images/medala.gif)bin801 -> 801 bytes
-rw-r--r--forum/skins/default/media/images/medala_on.gif (renamed from templates/content/images/medala_on.gif)bin957 -> 957 bytes
-rw-r--r--forum/skins/default/media/images/new.gif (renamed from templates/content/images/new.gif)bin635 -> 635 bytes
-rw-r--r--forum/skins/default/media/images/nophoto.png (renamed from templates/content/images/nophoto.png)bin696 -> 696 bytes
-rw-r--r--forum/skins/default/media/images/openid.gif (renamed from templates/content/images/openid.gif)bin910 -> 910 bytes
-rw-r--r--forum/skins/default/media/images/openid/aol.gif (renamed from templates/content/images/openid/aol.gif)bin2205 -> 2205 bytes
-rw-r--r--forum/skins/default/media/images/openid/blogger.ico (renamed from templates/content/images/openid/blogger.ico)bin3638 -> 3638 bytes
-rw-r--r--forum/skins/default/media/images/openid/claimid.ico (renamed from templates/content/images/openid/claimid.ico)bin3638 -> 3638 bytes
-rw-r--r--forum/skins/default/media/images/openid/facebook.gif (renamed from templates/content/images/openid/facebook.gif)bin2075 -> 2075 bytes
-rw-r--r--forum/skins/default/media/images/openid/flickr.ico (renamed from templates/content/images/openid/flickr.ico)bin1150 -> 1150 bytes
-rw-r--r--forum/skins/default/media/images/openid/google.gif (renamed from templates/content/images/openid/google.gif)bin1596 -> 1596 bytes
-rw-r--r--forum/skins/default/media/images/openid/livejournal.ico (renamed from templates/content/images/openid/livejournal.ico)bin5222 -> 5222 bytes
-rw-r--r--forum/skins/default/media/images/openid/myopenid.ico (renamed from templates/content/images/openid/myopenid.ico)bin2862 -> 2862 bytes
-rw-r--r--forum/skins/default/media/images/openid/openid-inputicon.gif (renamed from templates/content/images/openid/openid-inputicon.gif)bin237 -> 237 bytes
-rw-r--r--forum/skins/default/media/images/openid/openid.gif (renamed from templates/content/images/openid/openid.gif)bin740 -> 740 bytes
-rw-r--r--forum/skins/default/media/images/openid/technorati.ico (renamed from templates/content/images/openid/technorati.ico)bin2294 -> 2294 bytes
-rw-r--r--forum/skins/default/media/images/openid/verisign.ico (renamed from templates/content/images/openid/verisign.ico)bin4710 -> 4710 bytes
-rw-r--r--forum/skins/default/media/images/openid/vidoop.ico (renamed from templates/content/images/openid/vidoop.ico)bin1406 -> 1406 bytes
-rw-r--r--forum/skins/default/media/images/openid/wordpress.ico (renamed from templates/content/images/openid/wordpress.ico)bin1150 -> 1150 bytes
-rw-r--r--forum/skins/default/media/images/openid/yahoo.gif (renamed from templates/content/images/openid/yahoo.gif)bin1682 -> 1682 bytes
-rw-r--r--forum/skins/default/media/images/quest-bg.gif (renamed from templates/content/images/quest-bg.gif)bin294 -> 294 bytes
-rw-r--r--forum/skins/default/media/images/vote-accepted-on.png (renamed from templates/content/images/vote-accepted-on.png)bin1124 -> 1124 bytes
-rw-r--r--forum/skins/default/media/images/vote-accepted.png (renamed from templates/content/images/vote-accepted.png)bin1058 -> 1058 bytes
-rw-r--r--forum/skins/default/media/images/vote-arrow-down-on.png (renamed from templates/content/images/vote-arrow-down-on.png)bin905 -> 905 bytes
-rw-r--r--forum/skins/default/media/images/vote-arrow-down.png (renamed from templates/content/images/vote-arrow-down.png)bin876 -> 876 bytes
-rw-r--r--forum/skins/default/media/images/vote-arrow-up-on.png (renamed from templates/content/images/vote-arrow-up-on.png)bin906 -> 906 bytes
-rw-r--r--forum/skins/default/media/images/vote-arrow-up.png (renamed from templates/content/images/vote-arrow-up.png)bin843 -> 843 bytes
-rw-r--r--forum/skins/default/media/images/vote-favorite-off.png (renamed from templates/content/images/vote-favorite-off.png)bin930 -> 930 bytes
-rw-r--r--forum/skins/default/media/images/vote-favorite-on.png (renamed from templates/content/images/vote-favorite-on.png)bin1023 -> 1023 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/aol.gif (renamed from templates/content/jquery-openid/images/aol.gif)bin2205 -> 2205 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/blogger-1.png (renamed from templates/content/jquery-openid/images/blogger-1.png)bin432 -> 432 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/blogger.ico (renamed from templates/content/jquery-openid/images/blogger.ico)bin3638 -> 3638 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/claimid-0.png (renamed from templates/content/jquery-openid/images/claimid-0.png)bin629 -> 629 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/claimid.ico (renamed from templates/content/jquery-openid/images/claimid.ico)bin3638 -> 3638 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/facebook.gif (renamed from templates/content/jquery-openid/images/facebook.gif)bin2075 -> 2075 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/flickr.ico (renamed from templates/content/jquery-openid/images/flickr.ico)bin1150 -> 1150 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/flickr.png (renamed from templates/content/jquery-openid/images/flickr.png)bin426 -> 426 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/google.gif (renamed from templates/content/jquery-openid/images/google.gif)bin1596 -> 1596 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/livejournal-1.png (renamed from templates/content/jquery-openid/images/livejournal-1.png)bin713 -> 713 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/livejournal.ico (renamed from templates/content/jquery-openid/images/livejournal.ico)bin5222 -> 5222 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/myopenid-2.png (renamed from templates/content/jquery-openid/images/myopenid-2.png)bin511 -> 511 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/myopenid.ico (renamed from templates/content/jquery-openid/images/myopenid.ico)bin2862 -> 2862 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/openid-inputicon.gif (renamed from templates/content/jquery-openid/images/openid-inputicon.gif)bin237 -> 237 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/openid.gif (renamed from templates/content/jquery-openid/images/openid.gif)bin740 -> 740 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/openidico.png (renamed from templates/content/jquery-openid/images/openidico.png)bin654 -> 654 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/openidico16.png (renamed from templates/content/jquery-openid/images/openidico16.png)bin554 -> 554 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/technorati-1.png (renamed from templates/content/jquery-openid/images/technorati-1.png)bin606 -> 606 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/technorati.ico (renamed from templates/content/jquery-openid/images/technorati.ico)bin2294 -> 2294 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/verisign-2.png (renamed from templates/content/jquery-openid/images/verisign-2.png)bin859 -> 859 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/verisign.ico (renamed from templates/content/jquery-openid/images/verisign.ico)bin4710 -> 4710 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/vidoop.ico (renamed from templates/content/jquery-openid/images/vidoop.ico)bin1406 -> 1406 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/vidoop.png (renamed from templates/content/jquery-openid/images/vidoop.png)bin499 -> 499 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/wordpress.ico (renamed from templates/content/jquery-openid/images/wordpress.ico)bin1150 -> 1150 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/wordpress.png (renamed from templates/content/jquery-openid/images/wordpress.png)bin566 -> 566 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/images/yahoo.gif (renamed from templates/content/jquery-openid/images/yahoo.gif)bin1682 -> 1682 bytes
-rw-r--r--forum/skins/default/media/jquery-openid/jquery.openid.js (renamed from templates/content/jquery-openid/jquery.openid.js)0
-rw-r--r--forum/skins/default/media/jquery-openid/openid.css (renamed from templates/content/jquery-openid/openid.css)0
-rw-r--r--forum/skins/default/media/js/com.cnprog.admin.js (renamed from templates/content/js/com.cnprog.admin.js)0
-rw-r--r--forum/skins/default/media/js/com.cnprog.editor.js (renamed from templates/content/js/com.cnprog.editor.js)0
-rw-r--r--forum/skins/default/media/js/com.cnprog.i18n.js (renamed from templates/content/js/com.cnprog.i18n.js)0
-rw-r--r--forum/skins/default/media/js/com.cnprog.post.js (renamed from templates/content/js/com.cnprog.post.js)28
-rw-r--r--forum/skins/default/media/js/com.cnprog.tag_selector.js (renamed from templates/content/js/com.cnprog.tag_selector.js)6
-rw-r--r--forum/skins/default/media/js/com.cnprog.utils.js (renamed from templates/content/js/com.cnprog.utils.js)18
-rw-r--r--forum/skins/default/media/js/compress.bat (renamed from templates/content/js/compress.bat)0
-rw-r--r--forum/skins/default/media/js/excanvas.pack.js (renamed from templates/content/js/excanvas.pack.js)0
-rw-r--r--forum/skins/default/media/js/flot-build.bat (renamed from templates/content/js/flot-build.bat)0
-rw-r--r--forum/skins/default/media/js/jquery-1.2.6.js (renamed from templates/content/js/jquery-1.2.6.js)0
-rw-r--r--forum/skins/default/media/js/jquery-1.2.6.min.js (renamed from templates/content/js/jquery-1.2.6.min.js)0
-rw-r--r--forum/skins/default/media/js/jquery.ajaxfileupload.js (renamed from templates/content/js/jquery.ajaxfileupload.js)0
-rw-r--r--forum/skins/default/media/js/jquery.flot.js (renamed from templates/content/js/jquery.flot.js)0
-rw-r--r--forum/skins/default/media/js/jquery.flot.pack.js (renamed from templates/content/js/jquery.flot.pack.js)0
-rw-r--r--forum/skins/default/media/js/jquery.form.js (renamed from templates/content/js/jquery.form.js)0
-rw-r--r--forum/skins/default/media/js/jquery.i18n.js (renamed from templates/content/js/jquery.i18n.js)0
-rw-r--r--forum/skins/default/media/js/jquery.openid.js (renamed from templates/content/js/jquery.openid.js)2
-rw-r--r--forum/skins/default/media/js/jquery.validate.pack.js (renamed from templates/content/js/jquery.validate.pack.js)0
-rw-r--r--forum/skins/default/media/js/se_hilite.js (renamed from templates/content/js/se_hilite.js)0
-rw-r--r--forum/skins/default/media/js/se_hilite_src.js (renamed from templates/content/js/se_hilite_src.js)0
-rw-r--r--forum/skins/default/media/js/wmd/images/wmd-buttons.png (renamed from templates/content/js/wmd/images/wmd-buttons.png)bin7465 -> 7465 bytes
-rw-r--r--forum/skins/default/media/js/wmd/showdown-min.js (renamed from templates/content/js/wmd/showdown-min.js)0
-rw-r--r--forum/skins/default/media/js/wmd/showdown.js (renamed from templates/content/js/wmd/showdown.js)0
-rw-r--r--forum/skins/default/media/js/wmd/wmd-min.js1
-rw-r--r--forum/skins/default/media/js/wmd/wmd-test.html (renamed from templates/content/js/wmd/wmd-test.html)0
-rw-r--r--forum/skins/default/media/js/wmd/wmd.css (renamed from templates/content/js/wmd/wmd.css)0
-rw-r--r--forum/skins/default/media/js/wmd/wmd.js (renamed from templates/content/js/wmd/wmd.js)54
-rw-r--r--forum/skins/default/media/js/yuicompressor-2.4.2.jar (renamed from templates/content/js/yuicompressor-2.4.2.jar)bin851219 -> 851219 bytes
-rw-r--r--forum/skins/default/media/style/default.css (renamed from templates/content/style/default.css)6
-rw-r--r--forum/skins/default/media/style/jquery.autocomplete.css (renamed from templates/content/style/jquery.autocomplete.css)0
-rw-r--r--forum/skins/default/media/style/openid.css (renamed from templates/content/style/openid.css)0
-rw-r--r--forum/skins/default/media/style/prettify.css (renamed from templates/content/style/prettify.css)0
-rw-r--r--forum/skins/default/media/style/style.css2459
-rw-r--r--forum/skins/default/templates/404.html (renamed from templates/404.html)0
-rw-r--r--forum/skins/default/templates/500.html (renamed from templates/500.html)0
-rw-r--r--forum/skins/default/templates/about.html (renamed from templates/about.html)0
-rw-r--r--forum/skins/default/templates/answer_edit.html (renamed from templates/answer_edit.html)12
-rw-r--r--forum/skins/default/templates/answer_edit_tips.html (renamed from templates/answer_edit_tips.html)0
-rw-r--r--forum/skins/default/templates/ask.html (renamed from templates/ask.html)12
-rw-r--r--forum/skins/default/templates/authopenid/changeemail.html (renamed from templates/authopenid/changeemail.html)0
-rw-r--r--forum/skins/default/templates/authopenid/changeopenid.html (renamed from templates/authopenid/changeopenid.html)0
-rw-r--r--forum/skins/default/templates/authopenid/changepw.html (renamed from templates/authopenid/changepw.html)0
-rw-r--r--forum/skins/default/templates/authopenid/complete.html (renamed from templates/authopenid/complete.html)3
-rw-r--r--forum/skins/default/templates/authopenid/confirm_email.txt (renamed from templates/authopenid/confirm_email.txt)0
-rw-r--r--forum/skins/default/templates/authopenid/delete.html (renamed from templates/authopenid/delete.html)0
-rw-r--r--forum/skins/default/templates/authopenid/email_validation.txt (renamed from templates/authopenid/email_validation.txt)0
-rw-r--r--forum/skins/default/templates/authopenid/external_legacy_login_info.html (renamed from templates/authopenid/external_legacy_login_info.html)0
-rw-r--r--forum/skins/default/templates/authopenid/failure.html (renamed from templates/authopenid/failure.html)0
-rw-r--r--forum/skins/default/templates/authopenid/sendpw.html (renamed from templates/authopenid/sendpw.html)0
-rw-r--r--forum/skins/default/templates/authopenid/sendpw_email.txt (renamed from templates/authopenid/sendpw_email.txt)0
-rw-r--r--forum/skins/default/templates/authopenid/settings.html (renamed from templates/authopenid/settings.html)0
-rwxr-xr-xforum/skins/default/templates/authopenid/signin.html (renamed from templates/authopenid/signin.html)38
-rw-r--r--forum/skins/default/templates/authopenid/signup.html (renamed from templates/authopenid/signup.html)6
-rw-r--r--forum/skins/default/templates/authopenid/yadis.xrdf (renamed from templates/authopenid/yadis.xrdf)0
-rw-r--r--forum/skins/default/templates/badge.html (renamed from templates/badge.html)0
-rw-r--r--forum/skins/default/templates/badges.html (renamed from templates/badges.html)0
-rwxr-xr-xforum/skins/default/templates/base.html (renamed from templates/base.html)34
-rw-r--r--forum/skins/default/templates/base_content.html (renamed from templates/base_content.html)35
-rw-r--r--forum/skins/default/templates/book.html (renamed from templates/book.html)8
-rw-r--r--forum/skins/default/templates/close.html (renamed from templates/close.html)0
-rw-r--r--forum/skins/default/templates/edit_user_email_feeds_form.html (renamed from templates/edit_user_email_feeds_form.html)0
-rw-r--r--forum/skins/default/templates/faq.html (renamed from templates/faq.html)0
-rwxr-xr-xforum/skins/default/templates/fbconnect/xd_receiver.html10
-rw-r--r--forum/skins/default/templates/feedback.html (renamed from templates/feedback.html)0
-rw-r--r--forum/skins/default/templates/feedback_email.txt (renamed from templates/feedback_email.txt)0
-rw-r--r--forum/skins/default/templates/feeds/rss_description.html (renamed from templates/feeds/rss_description.html)0
-rw-r--r--forum/skins/default/templates/feeds/rss_title.html (renamed from templates/feeds/rss_title.html)0
-rw-r--r--forum/skins/default/templates/footer.html (renamed from templates/footer.html)2
-rw-r--r--forum/skins/default/templates/header.html (renamed from templates/header.html)2
-rwxr-xr-xforum/skins/default/templates/index.html124
-rwxr-xr-xforum/skins/default/templates/index_.html124
-rw-r--r--forum/skins/default/templates/logout.html (renamed from templates/logout.html)0
-rw-r--r--forum/skins/default/templates/notarobot.html (renamed from templates/notarobot.html)0
-rw-r--r--forum/skins/default/templates/pagesize.html (renamed from templates/pagesize.html)0
-rw-r--r--forum/skins/default/templates/paginator.html (renamed from templates/paginator.html)0
-rw-r--r--forum/skins/default/templates/post_contributor_info.html (renamed from templates/post_contributor_info.html)0
-rw-r--r--forum/skins/default/templates/privacy.html (renamed from templates/privacy.html)0
-rw-r--r--forum/skins/default/templates/question.html (renamed from templates/question.html)80
-rw-r--r--forum/skins/default/templates/question_edit.html (renamed from templates/question_edit.html)12
-rw-r--r--forum/skins/default/templates/question_edit_tips.html (renamed from templates/question_edit_tips.html)0
-rw-r--r--forum/skins/default/templates/question_retag.html (renamed from templates/question_retag.html)6
-rw-r--r--forum/skins/default/templates/question_summary_list_roll.html (renamed from templates/question_summary_list_roll.html)2
-rw-r--r--forum/skins/default/templates/questions.html (renamed from templates/questions.html)8
-rw-r--r--forum/skins/default/templates/reopen.html (renamed from templates/reopen.html)0
-rw-r--r--forum/skins/default/templates/revisions_answer.html (renamed from templates/revisions_answer.html)8
-rw-r--r--forum/skins/default/templates/revisions_question.html (renamed from templates/revisions_question.html)8
-rw-r--r--forum/skins/default/templates/tag_selector.html (renamed from templates/tag_selector.html)4
-rw-r--r--forum/skins/default/templates/tags.html (renamed from templates/tags.html)2
-rw-r--r--forum/skins/default/templates/user.html (renamed from templates/user.html)4
-rw-r--r--forum/skins/default/templates/user_edit.html (renamed from templates/user_edit.html)2
-rw-r--r--forum/skins/default/templates/user_email_subscriptions.html (renamed from templates/user_email_subscriptions.html)0
-rw-r--r--forum/skins/default/templates/user_favorites.html (renamed from templates/user_favorites.html)0
-rw-r--r--forum/skins/default/templates/user_footer.html (renamed from templates/user_footer.html)0
-rw-r--r--forum/skins/default/templates/user_info.html (renamed from templates/user_info.html)0
-rw-r--r--forum/skins/default/templates/user_recent.html (renamed from templates/user_recent.html)0
-rw-r--r--forum/skins/default/templates/user_reputation.html (renamed from templates/user_reputation.html)4
-rw-r--r--forum/skins/default/templates/user_responses.html (renamed from templates/user_responses.html)0
-rw-r--r--forum/skins/default/templates/user_stats.html (renamed from templates/user_stats.html)6
-rw-r--r--forum/skins/default/templates/user_tabs.html (renamed from templates/user_tabs.html)0
-rw-r--r--forum/skins/default/templates/user_votes.html (renamed from templates/user_votes.html)4
-rw-r--r--forum/skins/default/templates/users.html (renamed from templates/users.html)0
-rw-r--r--forum/skins/default/templates/users_questions.html (renamed from templates/users_questions.html)6
-rw-r--r--forum/templatetags/extra_filters.py5
-rw-r--r--forum/templatetags/extra_tags.py36
-rw-r--r--forum/upfiles/README2
-rw-r--r--forum/urls.py112
-rw-r--r--forum/user.py74
-rw-r--r--forum/user_messages/__init__.py (renamed from user_messages/__init__.py)0
-rw-r--r--forum/user_messages/context_processors.py (renamed from user_messages/context_processors.py)2
-rw-r--r--forum/utils/__init__.py (renamed from pgfulltext/__init__.py)0
-rw-r--r--forum/utils/cache.py (renamed from utils/cache.py)0
-rw-r--r--forum/utils/decorators.py (renamed from utils/decorators.py)0
-rw-r--r--forum/utils/diff.py (renamed from forum/diff.py)0
-rw-r--r--forum/utils/forms.py (renamed from utils/forms.py)0
-rw-r--r--forum/utils/html.py (renamed from utils/html.py)0
-rw-r--r--forum/utils/lists.py (renamed from utils/lists.py)0
-rw-r--r--forum/utils/odict.py (renamed from utils/odict.py)0
-rw-r--r--forum/views.py2412
-rw-r--r--forum/views/README12
-rw-r--r--forum/views/__init__.py5
-rw-r--r--forum/views/commands.py335
-rw-r--r--forum/views/meta.py91
-rw-r--r--forum/views/readers.py588
-rw-r--r--forum/views/users.py947
-rw-r--r--forum/views/writers.py442
-rwxr-xr-x[-rw-r--r--]forum_modules/__init__.py (renamed from utils/__init__.py)0
-rwxr-xr-xforum_modules/books/__init__.py3
-rwxr-xr-xforum_modules/books/models.py63
-rwxr-xr-xforum_modules/books/urls.py10
-rwxr-xr-xforum_modules/books/views.py142
-rwxr-xr-xforum_modules/pgfulltext/__init__.py9
-rwxr-xr-xforum_modules/pgfulltext/handlers.py11
-rwxr-xr-x[-rw-r--r--]forum_modules/pgfulltext/management.py (renamed from pgfulltext/management.py)18
-rwxr-xr-xforum_modules/pgfulltext/pg_fts_install.sql38
-rwxr-xr-xforum_modules/sphinxfulltext/DISABLED0
-rwxr-xr-xforum_modules/sphinxfulltext/__init__.py0
-rwxr-xr-xforum_modules/sphinxfulltext/dependencies.py2
-rwxr-xr-xforum_modules/sphinxfulltext/handlers.py4
-rwxr-xr-xforum_modules/sphinxfulltext/models.py10
-rwxr-xr-xforum_modules/sphinxfulltext/settings.py5
-rw-r--r--locale/en/LC_MESSAGES/django.mobin26619 -> 26986 bytes
-rw-r--r--locale/en/LC_MESSAGES/django.po2200
-rw-r--r--locale/es/LC_MESSAGES/django.mobin49713 -> 367 bytes
-rw-r--r--locale/es/LC_MESSAGES/django.po3213
-rwxr-xr-xosqa.iml19
-rw-r--r--session_messages/__init__.py37
-rw-r--r--session_messages/context_processors.py48
-rw-r--r--session_messages/models.py3
-rwxr-xr-xsettings.py14
-rwxr-xr-xsettings_local.py.dist21
-rw-r--r--sql_scripts/update_2010_02_22.sql1
-rw-r--r--templates/content/js/wmd/wmd-min.js1
-rw-r--r--templates/content/style/style.css1466
-rwxr-xr-xtemplates/fbconnect/xd_receiver.html1
-rw-r--r--templates/index.html164
-rw-r--r--user_messages/models.py3
283 files changed, 11385 insertions, 8622 deletions
diff --git a/.gitignore b/.gitignore
index 16d8a5f0..4ebb1e31 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,11 @@
*.pyc
*.swp
*.log
+osqa.wsgi
+nbproject
+settings_local.py
+.idea
+*.iml
env
nbproject
pip-log.txt
-settings_local.py
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100755
index 00000000..b9a1798a
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <resourceExtensions>
+ <entry name=".+\.(properties|xml|html|dtd|tld)" />
+ <entry name=".+\.(gif|png|jpeg|jpg)" />
+ </resourceExtensions>
+ <wildcardResourcePatterns>
+ <entry name="?*.properties" />
+ <entry name="?*.xml" />
+ <entry name="?*.gif" />
+ <entry name="?*.png" />
+ <entry name="?*.jpeg" />
+ <entry name="?*.jpg" />
+ <entry name="?*.html" />
+ <entry name="?*.dtd" />
+ <entry name="?*.tld" />
+ <entry name="?*.ftl" />
+ </wildcardResourcePatterns>
+ <annotationProcessing enabled="false" useClasspath="true" />
+ </component>
+</project>
+
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100755
index 00000000..b385f01f
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,5 @@
+<component name="CopyrightManager">
+ <settings default="">
+ <module2copyright />
+ </settings>
+</component> \ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100755
index 00000000..7c62b52a
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
+</project>
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100755
index 00000000..5253b461
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="AnalysisProjectProfileManager">
+ <option name="PROJECT_PROFILE" />
+ <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+ <list size="0" />
+ </component>
+ <component name="Archetypes.Configuration">
+ <option name="archetypesFileLocation" value="archetypes.xml" />
+ <option name="workingDirectory" value="" />
+ </component>
+ <component name="DependencyValidationManager">
+ <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+ </component>
+ <component name="HandyEdit.TapestrySupport" tapestryProject="false" askEnableTapestry="true" componentSpec="true">
+ <locations pageRoot="" componentRoot="" applicationFile="" pagePropertiesRoot="" />
+ <packages page="" component="" />
+ <completion properties="true" methods="false" htmlAttributes="true" />
+ <toolWindows htmlAttributes="true" />
+ <ext extensionComponentSpec="jwc" extensionPageSpec="page" extensionScript="script" />
+ <create />
+ </component>
+ <component name="IdProvider" IDEtalkID="119765BF5908F4A0676504FC73A9C8F4" />
+ <component name="JavadocGenerationManager">
+ <option name="OUTPUT_DIRECTORY" />
+ <option name="OPTION_SCOPE" value="protected" />
+ <option name="OPTION_HIERARCHY" value="true" />
+ <option name="OPTION_NAVIGATOR" value="true" />
+ <option name="OPTION_INDEX" value="true" />
+ <option name="OPTION_SEPARATE_INDEX" value="true" />
+ <option name="OPTION_DOCUMENT_TAG_USE" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
+ <option name="OPTION_DEPRECATED_LIST" value="true" />
+ <option name="OTHER_OPTIONS" value="" />
+ <option name="HEAP_SIZE" />
+ <option name="LOCALE" />
+ <option name="OPEN_IN_BROWSER" value="true" />
+ </component>
+ <component name="ProjectDetails">
+ <option name="projectName" value="osqa" />
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/out" />
+ </component>
+ <component name="SvnBranchConfigurationManager">
+ <option name="mySupportsUserInfoFilter" value="true" />
+ </component>
+ <component name="VssConfiguration">
+ <option name="CLIENT_PATH" value="" />
+ <option name="SRCSAFEINI_PATH" value="" />
+ <option name="USER_NAME" value="" />
+ <option name="PWD" value="" />
+ <CheckoutOptions>
+ <option name="COMMENT" value="" />
+ <option name="DO_NOT_GET_LATEST_VERSION" value="false" />
+ <option name="REPLACE_WRITABLE" value="false" />
+ <option name="RECURSIVE" value="false" />
+ </CheckoutOptions>
+ <CheckinOptions>
+ <option name="COMMENT" value="" />
+ <option name="KEEP_CHECKED_OUT" value="false" />
+ <option name="RECURSIVE" value="false" />
+ </CheckinOptions>
+ <AddOptions>
+ <option name="STORE_ONLY_LATEST_VERSION" value="false" />
+ <option name="CHECK_OUT_IMMEDIATELY" value="false" />
+ </AddOptions>
+ <UndocheckoutOptions>
+ <option name="MAKE_WRITABLE" value="false" />
+ <option name="REPLACE_LOCAL_COPY" value="2" />
+ <option name="RECURSIVE" value="false" />
+ </UndocheckoutOptions>
+ <GetOptions>
+ <option name="REPLACE_WRITABLE" value="0" />
+ <option name="MAKE_WRITABLE" value="false" />
+ <option name="ANSWER_NEGATIVELY" value="false" />
+ <option name="ANSWER_POSITIVELY" value="false" />
+ <option name="RECURSIVE" value="false" />
+ <option name="VERSION" />
+ </GetOptions>
+ </component>
+ <component name="WebServicesPlugin" addRequiredLibraries="true" />
+ <component name="masterDetails">
+ <states>
+ <state key="ArtifactsStructureConfigurable.UI">
+ <UIState>
+ <splitter-proportions>
+ <SplitterProportionsDataImpl />
+ </splitter-proportions>
+ <settings />
+ </UIState>
+ </state>
+ <state key="Copyright.UI">
+ <UIState>
+ <splitter-proportions>
+ <SplitterProportionsDataImpl />
+ </splitter-proportions>
+ </UIState>
+ </state>
+ <state key="ProjectJDKs.UI">
+ <UIState>
+ <splitter-proportions>
+ <SplitterProportionsDataImpl>
+ <option name="proportions">
+ <list>
+ <option value="0.2" />
+ </list>
+ </option>
+ </SplitterProportionsDataImpl>
+ </splitter-proportions>
+ <last-edited>1.6</last-edited>
+ </UIState>
+ </state>
+ <state key="ScopeChooserConfigurable.UI">
+ <UIState>
+ <splitter-proportions>
+ <SplitterProportionsDataImpl />
+ </splitter-proportions>
+ <settings />
+ </UIState>
+ </state>
+ </states>
+ </component>
+</project>
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100755
index 00000000..9a64d92a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/osqa.iml" filepath="$PROJECT_DIR$/osqa.iml" />
+ </modules>
+ </component>
+</project>
+
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100755
index 00000000..1e7cce4b
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Palette2">
+ <group name="Swing">
+ <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+ </item>
+ <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+ <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+ <initial-values>
+ <property name="text" value="Button" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="RadioButton" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="CheckBox" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="Label" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+ <preferred-size width="-1" height="20" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+ </item>
+ </group>
+ </component>
+</project>
+
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100755
index 00000000..7e76f0fd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="" vcs="Git" />
+ </component>
+</project>
+
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100755
index 00000000..c22d0818
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,687 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CCaseConfig">
+ <option name="checkoutReserved" value="false" />
+ <option name="markExternalChangeAsUpToDate" value="true" />
+ <option name="checkInUseHijack" value="true" />
+ <option name="useUcmModel" value="true" />
+ <option name="isOffline" value="false" />
+ <option name="synchOutside" value="false" />
+ <option name="isHistoryResticted" value="true" />
+ <option name="useIdenticalSwitch" value="true" />
+ <option name="synchActivitiesOnRefresh" value="true" />
+ <option name="lastScr" value="" />
+ <option name="scrTextFileName" value="" />
+ <option name="historyRevisionsNumber" value="4" />
+ </component>
+ <component name="ChangeListManager" verified="true">
+ <list default="true" readonly="true" id="df1b69cf-9c35-4a78-9890-c59b542399e8" name="Default" comment="">
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/fbconnect/views.py" afterPath="$PROJECT_DIR$/fbconnect/views.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/uiDesigner.xml" afterPath="$PROJECT_DIR$/.idea/uiDesigner.xml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/django_authopenid/urls.py" afterPath="$PROJECT_DIR$/django_authopenid/urls.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/settings.py" afterPath="$PROJECT_DIR$/settings.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/models/tag.py" afterPath="$PROJECT_DIR$/forum/models/tag.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/fbconnect/tests.py" afterPath="$PROJECT_DIR$/fbconnect/tests.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/WISH_LIST" afterPath="$PROJECT_DIR$/WISH_LIST" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/middleware/anon_user.py" afterPath="$PROJECT_DIR$/forum/middleware/anon_user.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/fbconnect/urls.py" afterPath="$PROJECT_DIR$/fbconnect/urls.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/models/question.py" afterPath="$PROJECT_DIR$/forum/models/question.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/models/meta.py" afterPath="$PROJECT_DIR$/forum/models/meta.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/modules.xml" afterPath="$PROJECT_DIR$/.idea/modules.xml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/copyright/profiles_settings.xml" afterPath="$PROJECT_DIR$/.idea/copyright/profiles_settings.xml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/models/__init__.py" afterPath="$PROJECT_DIR$/forum/models/__init__.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/models/answer.py" afterPath="$PROJECT_DIR$/forum/models/answer.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/rmpyc" afterPath="$PROJECT_DIR$/rmpyc" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/models/user.py" afterPath="$PROJECT_DIR$/forum/models/user.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/vcs.xml" afterPath="$PROJECT_DIR$/.idea/vcs.xml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/fbconnect/__init__.py" afterPath="$PROJECT_DIR$/fbconnect/__init__.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/models/base.py" afterPath="$PROJECT_DIR$/forum/models/base.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/django_authopenid/views.py" afterPath="$PROJECT_DIR$/django_authopenid/views.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/__init__.py" afterPath="$PROJECT_DIR$/forum/__init__.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/models/repute.py" afterPath="$PROJECT_DIR$/forum/models/repute.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/skins/default/templates/base.html" afterPath="$PROJECT_DIR$/forum/skins/default/templates/base.html" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/skins/default/templates/authopenid/signin.html" afterPath="$PROJECT_DIR$/forum/skins/default/templates/authopenid/signin.html" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/skins/default/templates/user_edit.html" afterPath="$PROJECT_DIR$/forum/skins/default/templates/user_edit.html" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/fbconnect/models.py" afterPath="$PROJECT_DIR$/fbconnect/models.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/settings_local.py.dist" afterPath="$PROJECT_DIR$/settings_local.py.dist" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/fbconnect/fb.py" afterPath="$PROJECT_DIR$/fbconnect/fb.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/osqa.iml" afterPath="$PROJECT_DIR$/osqa.iml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/tester/models.py" afterPath="$PROJECT_DIR$/tester/models.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/skins/default/templates/fbconnect/xd_receiver.html" afterPath="$PROJECT_DIR$/forum/skins/default/templates/fbconnect/xd_receiver.html" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/misc.xml" afterPath="$PROJECT_DIR$/.idea/misc.xml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/skins/default/templates/question.html" afterPath="$PROJECT_DIR$/forum/skins/default/templates/question.html" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/skins/default/templates/questions.html" afterPath="$PROJECT_DIR$/forum/skins/default/templates/questions.html" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/sql_scripts/update_2010_01_23.sql" afterPath="$PROJECT_DIR$/sql_scripts/update_2010_01_23.sql" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/forum/views/readers.py" afterPath="$PROJECT_DIR$/forum/views/readers.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/compiler.xml" afterPath="$PROJECT_DIR$/.idea/compiler.xml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/fbconnect/forms.py" afterPath="$PROJECT_DIR$/fbconnect/forms.py" />
+ <change type="DELETED" beforePath="C:\osqadev\updated\osqa\user_messages\models.py" afterPath="" />
+ <change type="MOVED" beforePath="C:\osqadev\updated\osqa\forum\diff.py" afterPath="$PROJECT_DIR$/forum/utils/diff.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/fbconnect/pjson.py" afterPath="$PROJECT_DIR$/fbconnect/pjson.py" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/encodings.xml" afterPath="$PROJECT_DIR$/.idea/encodings.xml" />
+ <change type="MODIFICATION" beforePath="$PROJECT_DIR$/tester/__init__.py" afterPath="$PROJECT_DIR$/tester/__init__.py" />
+ </list>
+ <ignored path="osqa.iws" />
+ <ignored path=".idea/workspace.xml" />
+ <option name="TRACKING_ENABLED" value="true" />
+ <option name="SHOW_DIALOG" value="false" />
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+ <option name="LAST_RESOLUTION" value="IGNORE" />
+ </component>
+ <component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
+ <component name="CreatePatchCommitExecutor">
+ <option name="PATCH_PATH" value="" />
+ <option name="REVERSE_PATCH" value="false" />
+ </component>
+ <component name="DaemonCodeAnalyzer">
+ <disable_hints />
+ </component>
+ <component name="DebuggerManager">
+ <breakpoint_any>
+ <breakpoint>
+ <option name="NOTIFY_CAUGHT" value="true" />
+ <option name="NOTIFY_UNCAUGHT" value="true" />
+ <option name="ENABLED" value="false" />
+ <option name="LOG_ENABLED" value="false" />
+ <option name="LOG_EXPRESSION_ENABLED" value="false" />
+ <option name="SUSPEND_POLICY" value="SuspendAll" />
+ <option name="COUNT_FILTER_ENABLED" value="false" />
+ <option name="COUNT_FILTER" value="0" />
+ <option name="CONDITION_ENABLED" value="false" />
+ <option name="CLASS_FILTERS_ENABLED" value="false" />
+ <option name="INSTANCE_FILTERS_ENABLED" value="false" />
+ <option name="CONDITION" value="" />
+ <option name="LOG_MESSAGE" value="" />
+ </breakpoint>
+ <breakpoint>
+ <option name="NOTIFY_CAUGHT" value="true" />
+ <option name="NOTIFY_UNCAUGHT" value="true" />
+ <option name="ENABLED" value="false" />
+ <option name="LOG_ENABLED" value="false" />
+ <option name="LOG_EXPRESSION_ENABLED" value="false" />
+ <option name="SUSPEND_POLICY" value="SuspendAll" />
+ <option name="COUNT_FILTER_ENABLED" value="false" />
+ <option name="COUNT_FILTER" value="0" />
+ <option name="CONDITION_ENABLED" value="false" />
+ <option name="CLASS_FILTERS_ENABLED" value="false" />
+ <option name="INSTANCE_FILTERS_ENABLED" value="false" />
+ <option name="CONDITION" value="" />
+ <option name="LOG_MESSAGE" value="" />
+ </breakpoint>
+ </breakpoint_any>
+ <breakpoint_rules />
+ <ui_properties />
+ </component>
+ <component name="FavoritesManager">
+ <favorites_list name="osqa" />
+ </component>
+ <component name="FileColors" enabled="true" enabledForTabs="true" />
+ <component name="FileEditorManager">
+ <leaf>
+ <file leaf-file-name="settings.py" pinned="false" current="false" current-in-tab="false">
+ <entry file="file://$PROJECT_DIR$/settings.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="71" column="20" selection-start="2573" selection-end="2573" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ <file leaf-file-name="anon_user.py" pinned="false" current="false" current-in-tab="false">
+ <entry file="file://$PROJECT_DIR$/forum/middleware/anon_user.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="3" column="35" selection-start="174" selection-end="174" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ <file leaf-file-name="cancel.py" pinned="false" current="false" current-in-tab="false">
+ <entry file="file://$PROJECT_DIR$/forum/middleware/cancel.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ <file leaf-file-name="pagesize.py" pinned="false" current="false" current-in-tab="false">
+ <entry file="file://$PROJECT_DIR$/forum/middleware/pagesize.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ <file leaf-file-name="__init__.py" pinned="false" current="false" current-in-tab="false">
+ <entry file="file://$PROJECT_DIR$/forum/middleware/__init__.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ <file leaf-file-name="__init__.py" pinned="false" current="false" current-in-tab="false">
+ <entry file="file://$PROJECT_DIR$/forum/user_messages/__init__.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ <file leaf-file-name="context_processors.py" pinned="false" current="true" current-in-tab="true">
+ <entry file="file://$PROJECT_DIR$/forum/user_messages/context_processors.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="14" column="27" selection-start="330" selection-end="330" vertical-scroll-proportion="0.3707165">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ <file leaf-file-name="__init__.py" pinned="false" current="false" current-in-tab="false">
+ <entry file="file://$PROJECT_DIR$/forum/__init__.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="34" selection-start="34" selection-end="34" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ <file leaf-file-name="readers.py" pinned="false" current="false" current-in-tab="false">
+ <entry file="file://$PROJECT_DIR$/forum/views/readers.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="23" column="38" selection-start="913" selection-end="913" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </file>
+ </leaf>
+ </component>
+ <component name="FindManager">
+ <FindUsagesManager>
+ <setting name="OPEN_NEW_TAB" value="false" />
+ </FindUsagesManager>
+ </component>
+ <component name="IdeDocumentHistory">
+ <option name="changedFiles">
+ <list>
+ <option value="$PROJECT_DIR$/settings_local.py" />
+ <option value="$PROJECT_DIR$/settings_local.py.dist" />
+ <option value="$PROJECT_DIR$/forum/models/answer.py" />
+ <option value="$PROJECT_DIR$/forum/models/question.py" />
+ <option value="$PROJECT_DIR$/django_authopenid/forms.py" />
+ <option value="$PROJECT_DIR$/forum/models/base.py" />
+ <option value="$PROJECT_DIR$/forum/views/writers.py" />
+ <option value="$PROJECT_DIR$/forum/views/commands.py" />
+ <option value="$PROJECT_DIR$/forum/views/users.py" />
+ <option value="$PROJECT_DIR$/forum/views/meta.py" />
+ <option value="$PROJECT_DIR$/forum/views/books.py" />
+ <option value="$PROJECT_DIR$/forum/views/readers.py" />
+ <option value="$PROJECT_DIR$/forum/__init__.py" />
+ <option value="$PROJECT_DIR$/settings.py" />
+ <option value="$PROJECT_DIR$/forum/middleware/anon_user.py" />
+ <option value="$PROJECT_DIR$/forum/user_messages/context_processors.py" />
+ </list>
+ </option>
+ </component>
+ <component name="ModuleEditorState">
+ <option name="LAST_EDITED_MODULE_NAME" />
+ <option name="LAST_EDITED_TAB_NAME" />
+ </component>
+ <component name="ProjectInspectionProfilesVisibleTreeState">
+ <entry key="Project Default">
+ <profile-state />
+ </entry>
+ </component>
+ <component name="ProjectLevelVcsManager">
+ <OptionsSetting value="true" id="Add" />
+ <OptionsSetting value="true" id="Remove" />
+ <OptionsSetting value="true" id="Checkout" />
+ <OptionsSetting value="true" id="Update" />
+ <OptionsSetting value="true" id="Status" />
+ <OptionsSetting value="true" id="Edit" />
+ <OptionsSetting value="true" id="Undo Check Out" />
+ <OptionsSetting value="true" id="Get Latest Version" />
+ <ConfirmationsSetting value="1" id="Add" />
+ <ConfirmationsSetting value="0" id="Remove" />
+ </component>
+ <component name="ProjectReloadState">
+ <option name="STATE" value="0" />
+ </component>
+ <component name="ProjectView">
+ <navigator currentView="ProjectPane" proportions="" version="1" splitterProportion="0.5">
+ <flattenPackages />
+ <showMembers />
+ <showModules />
+ <showLibraryContents />
+ <hideEmptyPackages />
+ <abbreviatePackageNames />
+ <autoscrollToSource />
+ <autoscrollFromSource />
+ <sortByType />
+ </navigator>
+ <panes>
+ <pane id="PackagesPane" />
+ <pane id="Favorites" />
+ <pane id="ProjectPane">
+ <subPane>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="osqa" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ <PATH>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="osqa" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+ </PATH_ELEMENT>
+ <PATH_ELEMENT>
+ <option name="myItemId" value="osqa" />
+ <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+ </PATH_ELEMENT>
+ </PATH>
+ </subPane>
+ </pane>
+ <pane id="Scope" />
+ </panes>
+ </component>
+ <component name="PropertiesComponent">
+ <property name="project.structure.last.edited" value="Libraries" />
+ <property name="GoToFile.includeJavaFiles" value="false" />
+ <property name="project.structure.proportion" value="0.0" />
+ <property name="options.splitter.main.proportions" value="0.3" />
+ <property name="MemberChooser.sorted" value="false" />
+ <property name="options.lastSelected" value="preferences.editor" />
+ <property name="project.structure.side.proportion" value="0.2" />
+ <property name="MemberChooser.copyJavadoc" value="false" />
+ <property name="GoToClass.toSaveIncludeLibraries" value="false" />
+ <property name="WebServerToolWindowFactoryState" value="false" />
+ <property name="MemberChooser.showClasses" value="true" />
+ <property name="GoToClass.includeLibraries" value="false" />
+ <property name="options.splitter.details.proportions" value="0.2" />
+ <property name="options.searchVisible" value="true" />
+ </component>
+ <component name="Regex">
+ <option name="pos1" value="218" />
+ <option name="pos2" value="218" />
+ <option name="pos3" value="162" />
+ <option name="pos4" value="444" />
+ <option name="pos5" value="162" />
+ <option name="autoUpdate" value="true" />
+ <option name="referenceOn" value="false" />
+ <option name="referencePos" value="0" />
+ <option name="showLabels" value="true" />
+ </component>
+ <component name="RunManager">
+ <configuration default="true" type="PythonUnitTestConfigurationType" factoryName="Python's unittest">
+ <option name="INTERPRETER_OPTIONS" value="" />
+ <option name="PARENT_ENVS" value="true" />
+ <option name="SDK_HOME" value="" />
+ <option name="WORKING_DIRECTORY" value="" />
+ <option name="IS_MODULE_SDK" value="false" />
+ <envs />
+ <module name="osqa" />
+ <option name="SCRIPT_NAME" value="" />
+ <option name="CLASS_NAME" value="" />
+ <option name="METHOD_NAME" value="" />
+ <option name="FOLDER_NAME" value="" />
+ <option name="TEST_TYPE" value="TEST_SCRIPT" />
+ <method>
+ <option name="AntTarget" enabled="false" />
+ <option name="BuildArtifacts" enabled="false" />
+ <option name="Make" enabled="true" />
+ <option name="Maven.BeforeRunTask" enabled="false" />
+ </method>
+ </configuration>
+ <configuration default="true" type="PythonConfigurationType" factoryName="Python">
+ <option name="INTERPRETER_OPTIONS" value="" />
+ <option name="PARENT_ENVS" value="true" />
+ <option name="SDK_HOME" value="" />
+ <option name="WORKING_DIRECTORY" value="" />
+ <option name="IS_MODULE_SDK" value="false" />
+ <envs>
+ <env name="PYTHONUNBUFFERED" value="1" />
+ </envs>
+ <module name="osqa" />
+ <option name="SCRIPT_NAME" value="" />
+ <option name="PARAMETERS" value="" />
+ <method>
+ <option name="AntTarget" enabled="false" />
+ <option name="BuildArtifacts" enabled="false" />
+ <option name="Make" enabled="true" />
+ <option name="Maven.BeforeRunTask" enabled="false" />
+ </method>
+ </configuration>
+ <configuration default="true" type="Remote" factoryName="Remote">
+ <option name="USE_SOCKET_TRANSPORT" value="true" />
+ <option name="SERVER_MODE" value="false" />
+ <option name="SHMEM_ADDRESS" value="javadebug" />
+ <option name="HOST" value="localhost" />
+ <option name="PORT" value="5005" />
+ <method>
+ <option name="AntTarget" enabled="false" />
+ <option name="BuildArtifacts" enabled="false" />
+ <option name="Maven.BeforeRunTask" enabled="false" />
+ </method>
+ </configuration>
+ <configuration default="true" type="py.test" factoryName="py.test">
+ <option name="INTERPRETER_OPTIONS" value="" />
+ <option name="PARENT_ENVS" value="true" />
+ <option name="SDK_HOME" value="" />
+ <option name="WORKING_DIRECTORY" value="" />
+ <option name="IS_MODULE_SDK" value="false" />
+ <envs />
+ <module name="osqa" />
+ <option name="testToRun" value="" />
+ <option name="keywords" value="" />
+ <method>
+ <option name="AntTarget" enabled="false" />
+ <option name="BuildArtifacts" enabled="false" />
+ <option name="Make" enabled="true" />
+ <option name="Maven.BeforeRunTask" enabled="false" />
+ </method>
+ </configuration>
+ <configuration default="true" type="Applet" factoryName="Applet">
+ <module name="" />
+ <option name="MAIN_CLASS_NAME" />
+ <option name="HTML_FILE_NAME" />
+ <option name="HTML_USED" value="false" />
+ <option name="WIDTH" value="400" />
+ <option name="HEIGHT" value="300" />
+ <option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
+ <option name="VM_PARAMETERS" />
+ <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+ <option name="ALTERNATIVE_JRE_PATH" />
+ <method>
+ <option name="AntTarget" enabled="false" />
+ <option name="BuildArtifacts" enabled="false" />
+ <option name="Make" enabled="true" />
+ <option name="Maven.BeforeRunTask" enabled="false" />
+ </method>
+ </configuration>
+ <configuration default="true" type="Application" factoryName="Application">
+ <extension name="coverage" enabled="false" merge="false" />
+ <extension name="snapshooter" />
+ <option name="MAIN_CLASS_NAME" />
+ <option name="VM_PARAMETERS" />
+ <option name="PROGRAM_PARAMETERS" />
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+ <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+ <option name="ALTERNATIVE_JRE_PATH" />
+ <option name="ENABLE_SWING_INSPECTOR" value="false" />
+ <option name="ENV_VARIABLES" />
+ <option name="PASS_PARENT_ENVS" value="true" />
+ <module name="" />
+ <envs />
+ <method>
+ <option name="AntTarget" enabled="false" />
+ <option name="BuildArtifacts" enabled="false" />
+ <option name="Make" enabled="true" />
+ <option name="Maven.BeforeRunTask" enabled="false" />
+ </method>
+ </configuration>
+ <configuration default="true" type="JUnit" factoryName="JUnit">
+ <extension name="coverage" enabled="false" merge="false" />
+ <extension name="snapshooter" />
+ <module name="" />
+ <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+ <option name="ALTERNATIVE_JRE_PATH" />
+ <option name="PACKAGE_NAME" />
+ <option name="MAIN_CLASS_NAME" />
+ <option name="METHOD_NAME" />
+ <option name="TEST_OBJECT" value="class" />
+ <option name="VM_PARAMETERS" />
+ <option name="PARAMETERS" />
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
+ <option name="ENV_VARIABLES" />
+ <option name="PASS_PARENT_ENVS" value="true" />
+ <option name="TEST_SEARCH_SCOPE">
+ <value defaultName="moduleWithDependencies" />
+ </option>
+ <envs />
+ <method>
+ <option name="AntTarget" enabled="false" />
+ <option name="BuildArtifacts" enabled="false" />
+ <option name="Make" enabled="true" />
+ <option name="Maven.BeforeRunTask" enabled="false" />
+ </method>
+ </configuration>
+ <list size="0" />
+ <configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
+ <Host>localhost</Host>
+ <Port>5050</Port>
+ </configuration>
+ </component>
+ <component name="ShelveChangesManager" show_recycled="false" />
+ <component name="StarteamConfiguration">
+ <option name="SERVER" value="" />
+ <option name="PORT" value="49201" />
+ <option name="USER" value="" />
+ <option name="PASSWORD" value="" />
+ <option name="PROJECT" value="" />
+ <option name="VIEW" value="" />
+ <option name="ALTERNATIVE_WORKING_PATH" value="" />
+ <option name="LOCK_ON_CHECKOUT" value="false" />
+ <option name="UNLOCK_ON_CHECKIN" value="false" />
+ </component>
+ <component name="SvnConfiguration">
+ <option name="USER" value="" />
+ <option name="PASSWORD" value="" />
+ <option name="LAST_MERGED_REVISION" />
+ <option name="UPDATE_RUN_STATUS" value="false" />
+ <option name="MERGE_DRY_RUN" value="false" />
+ <option name="MERGE_DIFF_USE_ANCESTRY" value="true" />
+ <option name="UPDATE_LOCK_ON_DEMAND" value="false" />
+ <option name="IGNORE_SPACES_IN_MERGE" value="false" />
+ <option name="DETECT_NESTED_COPIES" value="false" />
+ <option name="IGNORE_SPACES_IN_ANNOTATE" value="true" />
+ <option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" />
+ <configuration useDefault="true">F:\Users\COOL\AppData\Roaming\Subversion</configuration>
+ <myIsUseDefaultProxy>false</myIsUseDefaultProxy>
+ <supportedVersion>125</supportedVersion>
+ </component>
+ <component name="TaskManager">
+ <task active="true" id="Default" summary="Default task">
+ <created>1266447811437</created>
+ <updated>1266447811437</updated>
+ </task>
+ <servers />
+ </component>
+ <component name="TodoView" selected-index="0">
+ <todo-panel id="selected-file">
+ <are-packages-shown value="false" />
+ <are-modules-shown value="false" />
+ <flatten-packages value="false" />
+ <is-autoscroll-to-source value="true" />
+ </todo-panel>
+ <todo-panel id="all">
+ <are-packages-shown value="true" />
+ <are-modules-shown value="false" />
+ <flatten-packages value="false" />
+ <is-autoscroll-to-source value="true" />
+ </todo-panel>
+ <todo-panel id="default-changelist">
+ <are-packages-shown value="false" />
+ <are-modules-shown value="false" />
+ <flatten-packages value="false" />
+ <is-autoscroll-to-source value="false" />
+ </todo-panel>
+ </component>
+ <component name="ToolWindowManager">
+ <frame x="1277" y="-3" width="1286" height="806" extended-state="6" />
+ <editor active="false" />
+ <layout>
+ <window_info id="Archetypes" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.329927" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+ <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+ <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
+ <window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.329927" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
+ <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="true" content_ui="tabs" />
+ <window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+ <window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.24959481" sideWeight="0.659854" order="0" side_tool="false" content_ui="tabs" />
+ <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
+ <window_info id="Regex" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
+ <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
+ <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+ <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
+ <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
+ </layout>
+ </component>
+ <component name="VcsManagerConfiguration">
+ <option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" />
+ <option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" />
+ <option name="PERFORM_UPDATE_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_COMMIT_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_EDIT_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_CHECKOUT_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true" />
+ <option name="PERFORM_ROLLBACK_IN_BACKGROUND" value="false" />
+ <option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="true" />
+ <option name="FORCE_NON_EMPTY_COMMENT" value="false" />
+ <option name="LAST_COMMIT_MESSAGE" />
+ <option name="MAKE_NEW_CHANGELIST_ACTIVE" value="true" />
+ <option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" />
+ <option name="CHECK_FILES_UP_TO_DATE_BEFORE_COMMIT" value="false" />
+ <option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" />
+ <option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" />
+ <option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" />
+ <option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" />
+ <option name="ACTIVE_VCS_NAME" />
+ <option name="UPDATE_GROUP_BY_PACKAGES" value="false" />
+ <option name="UPDATE_GROUP_BY_CHANGELIST" value="false" />
+ <option name="SHOW_FILE_HISTORY_AS_TREE" value="false" />
+ <option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" />
+ </component>
+ <component name="XDebuggerManager">
+ <breakpoint-manager />
+ </component>
+ <component name="editorHistoryManager">
+ <entry file="file://$PROJECT_DIR$/forum/models/answer.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="24" column="54" selection-start="758" selection-end="758" vertical-scroll-proportion="0.1863354">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/views/commands.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="10" column="44" selection-start="413" selection-end="413" vertical-scroll-proportion="0.26397514">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/views/writers.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="12" column="0" selection-start="509" selection-end="553" vertical-scroll-proportion="-8.16">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/views/users.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="9" column="44" selection-start="539" selection-end="539" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/views/meta.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="5" column="44" selection-start="295" selection-end="295" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/views/books.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="23" column="80" selection-start="867" selection-end="867" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/urls.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="-1.666149">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/views/readers.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="23" column="38" selection-start="913" selection-end="913" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/__init__.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="34" selection-start="34" selection-end="34" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/settings.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="71" column="20" selection-start="2573" selection-end="2573" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/middleware/anon_user.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="3" column="35" selection-start="174" selection-end="174" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/middleware/cancel.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/middleware/pagesize.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/middleware/__init__.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/user_messages/__init__.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ <entry file="file://$PROJECT_DIR$/forum/user_messages/context_processors.py">
+ <provider selected="true" editor-type-id="text-editor">
+ <state line="14" column="27" selection-start="330" selection-end="330" vertical-scroll-proportion="0.3707165">
+ <folding />
+ </state>
+ </provider>
+ </entry>
+ </component>
+</project>
+
diff --git a/INSTALL b/INSTALL
index 8ecabb88..f70b3ec7 100644
--- a/INSTALL
+++ b/INSTALL
@@ -4,7 +4,7 @@ A. PREREQUISITES
B. INSTALLATION
1. Settings file
2. Database
- 3. Running CNPROG in the development server
+ 3. Running OSQA in the development server
4. Installation under Apache/WSGI
5. Full text search
6. Email subscriptions
@@ -16,34 +16,27 @@ D. CUSTOMIZATION
A. PREREQUISITES
-----------------------------------------------
-
-You can either follow the directions in this section, or you can read
-INSTALL.pip, which is simpler and creates a sandbox for your Python
-environment.
-
0. We recommend you to use python-setuptools to install pre-requirement libraries.
If you haven't installed it, please try to install it first.
e.g, sudo apt-get install python-setuptools
1. Python2.5/2.6, MySQL, Django v1.0/1.1
Note: email subscription sender job requires Django 1.1, everything else works with 1.0
- sudo apt-get install mysql-client mysql-server
-
Make sure mysql for python provider has been installed.
- sudo easy_install mysql-python
+sudo easy_install mysql-python
2. Python-openid v2.2
http://openidenabled.com/python-openid/
- sudo easy_install python-openid
+sudo easy_install python-openid
4. html5lib
http://code.google.com/p/html5lib/
Used for HTML sanitizer
- sudo easy_install html5lib
+sudo easy_install html5lib
5. Markdown2
http://code.google.com/p/python-markdown2/
- sudo easy_install markdown2
+sudo easy_install markdown2
6. Django Debug Toolbar
http://github.com/robhudson/django-debug-toolbar/tree/master
@@ -63,7 +56,7 @@ Notice that you will need to register with recaptcha.net and receive
recaptcha public and private keys that need to be saved in your
settings_local.py file
-NOTES: django_authopenid is included into CNPROG code
+NOTES: django_authopenid is included into OSQA code
and is significantly modified. http://code.google.com/p/django-authopenid/
no need to install this library
@@ -71,27 +64,16 @@ B. INSTALLATION
-----------------------------------------------
0. Make sure you have all above python libraries installed.
- Choose a location for the site and log files inside of this source
- code directory:
- export SOURCE=`pwd`
- export CNPROG=$SOURCE/CNPROG
- export CNLOG=$SOURCE/log
- And make some locations:
- mkdir -p $CNPROG/upfiles/
- mkdir -p $CNLOG
- Although it might be better, according to the Pinax forums, to have
- these locations somewhere else.
-
- Make cnprog installation server-readable on Linux command might be:
- sudo chown -R `whoami`:www-data $CNPROG
- sudo chown -R `whoami`:www-data $CNLOG
- It is assumed that webserver runs under group named "www-data".
- Perhaps on your system it is "apache".
-
- Directories $CNPROG/upfiles/ and $CNLOG must be server writable.
- [What about templates/ ?]
- chmod -R g+w $CNPROG/upfiles
- chmod -R g+w $CNLOG
+ make osqa installation server-readable on Linux command might be:
+ chown -R yourlogin:apache /path/to/OSQA
+
+ directories templates/upfiles and log must be server writable
+
+ on Linux type chmod
+ chmod -R g+w /path/to/OSQA/upfiles
+ chmod -R g+w /path/to/log
+
+ above it is assumed that webserver runs under group named "apache"
1. Settings file
@@ -99,20 +81,17 @@ Copy settings_local.py.dist to settings_local.py and
update all your settings. Check settings.py and update
it as well if necessory.
Section C explains configuration paramaters.
-I don't recommend sqlite3 because it doesn't seem to work on my system.
2. Database
- Prepare your database by using the same database/account
- configuration from settings_local.py, for example:
-
- sudo mysql
- create database osqa DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci;
- grant all on osqa.* to 'osqa'@'localhost';
-
- Run "python manage.py syncdb" to synchronize your database.
+Prepare your database by using the same database/account
+configuration from above.
+e.g,
+create database osqa DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci;
+grant all on osqa.* to 'osqa'@'localhost';
+And then run "python manage.py syncdb" to synchronize your database.
-3. Running CNPROG on the development server
+3. Running OSQA on the development server
Run "python manage.py runserver" to startup django
development environment.
@@ -133,8 +112,8 @@ import os
import sys
sys.path.insert(0,'/one/level/above') #insert to make sure that forum will be found
-sys.path.append('/one/level/above/CNPROG') #maybe this is not necessary
-os.environ['DJANGO_SETTINGS_MODULE'] = 'CNPROG.settings'
+sys.path.append('/one/level/above/OSQA') #maybe this is not necessary
+os.environ['DJANGO_SETTINGS_MODULE'] = 'OSQA.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
-----------
@@ -159,26 +138,26 @@ WSGIPythonEggs /var/python/eggs #must be readable and writable by apache
#this allows "rooting" forum at http://example.com/forum, if you like
<VirtualHost ...your ip...:80>
ServerAdmin forum@example.com
- DocumentRoot /path/to/cnprog
+ DocumentRoot /path/to/osqa-site
ServerName example.com
#run mod_wsgi process for django in daemon mode
#this allows avoiding confused timezone settings when
#another application runs in the same virtual host
- WSGIDaemonProcess CNPROG
- WSGIProcessGroup CNPROG
+ WSGIDaemonProcess OSQA
+ WSGIProcessGroup OSQA
#force all content to be served as static files
#otherwise django will be crunching images through itself wasting time
- Alias /content/ /path/to/cnprog/templates/content/
- AliasMatch /([^/]*\.css) /path/to/cnprog/templates/content/style/$1
- <Directory /path/to/cnprog/templates/content>
+ Alias /m/ /path/to/osqa-site/forum/skins/
+ Alias /upfiles/ /path/to/osqa-site/forum/upfiles/
+ <Directory /path/to/osqa-site/forum/skins>
Order deny,allow
Allow from all
</Directory>
#this is your wsgi script described in the prev section
- WSGIScriptAlias / /path/to/cnprog/cnprog.wsgi
+ WSGIScriptAlias / /path/to/osqa-site/osqa.wsgi
#this will force admin interface to work only
#through https (optional)
@@ -187,35 +166,39 @@ WSGIPythonEggs /var/python/eggs #must be readable and writable by apache
RewriteEngine on
RewriteRule /nimda(.*)$ https://example.com/nimda$1 [L,R=301]
</Location>
- CustomLog /var/log/httpd/CNPROG/access_log common
- ErrorLog /var/log/httpd/CNPROG/error_log
+ CustomLog /var/log/httpd/OSQA/access_log common
+ ErrorLog /var/log/httpd/OSQA/error_log
</VirtualHost>
#(optional) run admin interface under https
<VirtualHost ..your ip..:443>
ServerAdmin forum@example.com
- DocumentRoot /path/to/cnrpog
+ DocumentRoot /path/to/osqa-site
ServerName example.com
SSLEngine on
SSLCertificateFile /path/to/ssl-certificate/server.crt
SSLCertificateKeyFile /path/to/ssl-certificate/server.key
- WSGIScriptAlias / /path/to/cnprogcnprog.wsgi
- CustomLog /var/log/httpd/CNPROG/access_log common
- ErrorLog /var/log/httpd/CNPROG/error_log
+ WSGIScriptAlias / /path/to/osqa-site/osqa.wsgi
+ CustomLog /var/log/httpd/OSQA/access_log common
+ ErrorLog /var/log/httpd/OSQA/error_log
DirectoryIndex index.html
</VirtualHost>
-------------
5. Full text search (using sphinx search)
+
Currently full text search works only with sphinx search engine
+ And builtin PostgreSQL (postgres only >= 8.3???)
+
+ 5.1 Instructions for Sphinx search setup
Sphinx at this time supports only MySQL and PostgreSQL databases
to enable this, install sphinx search engine and djangosphinx
configure sphinx, sample configuration can be found in
sphinx/sphinx.conf file usually goes somewhere in /etc tree
- build cnprog index first time manually
+ build osqa index first time manually
- % indexer --config /path/to/sphinx.conf --index cnprog
+ % indexer --config /path/to/sphinx.conf --index osqa
setup cron job to rebuild index periodically with command
your crontab entry may be something like
@@ -242,7 +225,7 @@ WSGIPythonEggs /var/python/eggs #must be readable and writable by apache
This function at the moment requires Django 1.1
- edit paths in the file cron/send_email_alerts.
+ edit paths in the file cron/send_email_alerts
set up a cron job to call cron/send_email_alerts once or twice a day
subscription sender may be tested manually in shell
by calling cron/send_email_alerts
@@ -263,28 +246,6 @@ There are some demo scripts under sql_scripts folder,
including badges and test accounts for CNProg.com. You
don't need them to run your sample.
-9. Badges
-
- 1. You should run the SQL commands in:
-
- sql_scripts/badges.sql
-
- 2. Edit paths in the file `cron/multi_award_badges`. (This
- file doesn't yet exist in the git repositories, so just
- copy `cron/send_email_alerts` and make sure the command
- `multi_award_badges` is executed.)
-
- 3. Run `cron/multi_award_badges` to make sure it works okay.
-
- 4. Use `crontab -e` to call `cron/multi_award_badges` maybe four
- times an hour.
-
- 4,19,34,49 * * * * ~/webapps/osqa_server/projects/MYOSQA/cron/multi_award_badges
-
- 5. Repeat steps 1-4 for `cron/once_award_badges`.
-
-
-
C. CONFIGURATION PARAMETERS
#the only parameter that needs to be touched in settings.py is
@@ -292,8 +253,8 @@ DEBUG=False #set to True to enable debug mode
#all forum parameters are set in file settings_local.py
-LOG_FILENAME = 'cnprog.log' #where logging messages should go
-DATABASE_NAME = 'cnprog' # Or path to database file if using sqlite3.
+LOG_FILENAME = 'osqa.log' #where logging messages should go
+DATABASE_NAME = 'osqa' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_ENGINE = 'mysql' #mysql, etc
@@ -301,19 +262,19 @@ SERVER_EMAIL = ''
DEFAULT_FROM_EMAIL = ''
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = '' #not necessary if mailserver is run on local machine
-EMAIL_SUBJECT_PREFIX = '[CNPROG] '
-EMAIL_HOST='cnprog.com'
+EMAIL_SUBJECT_PREFIX = '[OSQA] '
+EMAIL_HOST='osqa.com'
EMAIL_PORT='25'
EMAIL_USE_TLS=False
TIME_ZONE = 'America/Tijuana'
-APP_TITLE = u'CNPROG Q&A Forum' #title of your forum
-APP_KEYWORDS = u'CNPROG,forum,community' #keywords for search engines
+APP_TITLE = u'OSQA Q&A Forum' #title of your forum
+APP_KEYWORDS = u'OSQA,forum,community' #keywords for search engines
APP_DESCRIPTION = u'Ask and answer questions.' #site description for searche engines
APP_INTRO = u'<p>Ask and answer questions, make the world better!</p>' #slogan that goes to front page in logged out mode
APP_COPYRIGHT = '' #copyright message
#if you set FORUM_SCRIPT_ALIAS= 'forum/'
-#then CNPROG will run at url http://example.com/forum
+#then OSQA will run at url http://example.com/forum
#FORUM_SCRIPT_ALIAS cannot have leading slash, otherwise it can be set to anything
FORUM_SCRIPT_ALIAS = '' #no leading slash, default = '' empty string
@@ -321,7 +282,7 @@ LANGUAGE_CODE = 'en' #forum language (see language instructions on the wiki)
EMAIL_VALIDATION = 'off' #string - on|off
MIN_USERNAME_LENGTH = 1
EMAIL_UNIQUE = False #if True, email addresses must be unique in all accounts
-APP_URL = 'http://cnprog.com' #used by email notif system and RSS
+APP_URL = 'http://osqa.com' #used by email notif system and RSS
GOOGLE_SITEMAP_CODE = '' #code for google site crawler (look up google webmaster tools)
GOOGLE_ANALYTICS_KEY = '' #key to enable google analytics on this site
BOOKS_ON = False #if True - books tab will be on
@@ -330,10 +291,10 @@ WIKI_ON = True #if False - community wiki feature is disabled
#experimental - allow password login through external site
#must implement django_authopenid/external_login.py
#included prototype external_login works with Mediawiki
-USE_EXTERNAL_LEGACY_LOGIN = True #if false CNPROG uses it's own login/password
-EXTERNAL_LEGACY_LOGIN_HOST = 'login.cnprog.com'
+USE_EXTERNAL_LEGACY_LOGIN = True #if false OSQA uses it's own login/password
+EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.com'
EXTERNAL_LEGACY_LOGIN_PORT = 80
-EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = '<span class="orange">CNPROG</span>'
+EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = '<span class="orange">OSQA</span>'
FEEDBACK_SITE_URL = None #None or url
LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
@@ -346,7 +307,7 @@ D. Customization
Other than settings_local.py the following will most likely need customization:
* locale/*/django.po - language files that may also contain your site-specific messages
if you want to start with english messages file - look for words like "forum" and
- "CNPROG" in the msgstr lines
+ "OSQA" in the msgstr lines
* templates/header.html and templates/footer.html may contain extra links
* templates/about.html - a place to explain for is your forum for
* templates/faq.html - put answers to users frequent questions
diff --git a/PENDING b/PENDING
new file mode 100644
index 00000000..9d2f00ee
--- /dev/null
+++ b/PENDING
@@ -0,0 +1,31 @@
+There are two kinds of things that can be done:
+refactorings (think of jogging in the morning, going to a spa, well make the code better :)
+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
+* set up loading of default settings from inside the /forum dir
+* automatic dependency checking for modules
+* propose how to rename directory forum --> osqa
+ without breaking things and keeping name of the project root
+ named the same way - osqa
+
+==New features==
+Whoever wants - pick a feature from the WISH_LIST
+add it here and start working on it
+If you are not starting immediately - leave it on the wishlist :)
+
+==Notes==
+1)after this is done most new suggested features
+ may be worked on easily since most of them
+ only require editing view functions and templates
+
+ However, anyone can work on new features anyway - you'll
+ just have to probably copy-paste your code into
+ the branch undergoing refactoring which involves
+ splitting the files. Auto merging across split points
+ is harder or impossible.
diff --git a/TODO b/WISH_LIST
index 28b220dc..3df3c307 100644
--- a/TODO
+++ b/WISH_LIST
@@ -1,6 +1,14 @@
-* per-tag email subscriptions
-* make sorting tabs work in question search - done
+* The wonder bar (integrated the search / ask functionality)
+* The authentication system ???
* allow multiple logins to the same account
+* allow multiple logins to the same account
+* more advanced templating/skinning system
+* per-tag email subscriptions
+* view for personalized news on the site
+* a little flag popping when there are news
+* drill-down mode for navigation by tags
+* improved admin console
+* sort out mess with profile - currently we patch django User
* Some functionality should be moved out of the forums app, in the case
that the forum app is restricted only to authenticated users:
diff --git a/context.py b/context.py
index cc7f26cb..21b9b64b 100644
--- a/context.py
+++ b/context.py
@@ -18,6 +18,7 @@ def application_settings(context):
'USE_EXTERNAL_LEGACY_LOGIN':settings.USE_EXTERNAL_LEGACY_LOGIN,
'RESOURCE_REVISION':settings.RESOURCE_REVISION,
'USE_SPHINX_SEARCH':settings.USE_SPHINX_SEARCH,
+ 'OSQA_SKIN':settings.OSQA_DEFAULT_SKIN,
}
return {'settings':my_settings}
diff --git a/cron/send_email_alerts b/cron/send_email_alerts
index 6c9e154d..6358b599 100755..100644
--- a/cron/send_email_alerts
+++ b/cron/send_email_alerts
@@ -1,10 +1,4 @@
-#!/bin/sh
-
-WORKON_HOME=~/envs/osqa
-PROJECT_ROOT=~/webapps/osqa_server/projects/osqa/
-
-# activate virtual environment
-. $WORKON_HOME/bin/activate
-
-cd $PROJECT_ROOT
-python manage.py send_email_alerts >> $PROJECT_ROOT/log/cron_mail.log 2>&1
+PYTHONPATH=/path/to/dir/above/forum
+export PYTHONPATH
+APP_ROOT=$PYTHONPATH/nmr-forum2
+/path/to/python $APP_ROOT/manage.py send_email_alerts
diff --git a/cron/send_email_alerts_virtualenv b/cron/send_email_alerts_virtualenv
new file mode 100644
index 00000000..6c9e154d
--- /dev/null
+++ b/cron/send_email_alerts_virtualenv
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+WORKON_HOME=~/envs/osqa
+PROJECT_ROOT=~/webapps/osqa_server/projects/osqa/
+
+# activate virtual environment
+. $WORKON_HOME/bin/activate
+
+cd $PROJECT_ROOT
+python manage.py send_email_alerts >> $PROJECT_ROOT/log/cron_mail.log 2>&1
diff --git a/django_authopenid/forms.py b/django_authopenid/forms.py
index 5ec21c1c..2f34986c 100644
--- a/django_authopenid/forms.py
+++ b/django_authopenid/forms.py
@@ -39,7 +39,7 @@ import types
import re
from django.utils.safestring import mark_safe
from recaptcha_django import ReCaptchaField
-from utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
+from forum.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
EXTERNAL_LOGIN_APP = settings.LOAD_EXTERNAL_LOGIN_APP()
# needed for some linux distributions like debian
@@ -48,7 +48,7 @@ try:
except ImportError:
from yadis import xri
-from utils.forms import clean_next
+from forum.utils.forms import clean_next
from django_authopenid.models import ExternalLoginData
__all__ = ['OpenidSigninForm', 'ClassicLoginForm', 'OpenidVerifyForm',
diff --git a/django_authopenid/util.py b/django_authopenid/util.py
index 969af0b9..cd2c2e2c 100644
--- a/django_authopenid/util.py
+++ b/django_authopenid/util.py
@@ -15,7 +15,7 @@ except:
from yadis import xri
import time, base64, hashlib, operator, logging
-from utils.forms import clean_next, get_next_url
+from forum.utils.forms import clean_next, get_next_url
from models import Association, Nonce
diff --git a/django_authopenid/views.py b/django_authopenid/views.py
index 16a78864..7c7d9e07 100755
--- a/django_authopenid/views.py
+++ b/django_authopenid/views.py
@@ -67,7 +67,7 @@ from django_authopenid.forms import OpenidSigninForm, ClassicLoginForm, OpenidRe
OpenidVerifyForm, ClassicRegisterForm, ChangePasswordForm, ChangeEmailForm, \
ChangeopenidForm, DeleteForm, EmailPasswordForm
import logging
-from utils.forms import get_next_url
+from forum.utils.forms import get_next_url
EXTERNAL_LOGIN_APP = settings.LOAD_EXTERNAL_LOGIN_APP()
diff --git a/fbconnect/fb.py b/fbconnect/fb.py
index 99bc0b79..afcd8210 100755
--- a/fbconnect/fb.py
+++ b/fbconnect/fb.py
@@ -73,17 +73,24 @@ STATES = {
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/urls.py b/fbconnect/urls.py
index 9b4ff0c9..bf2d4364 100755
--- a/fbconnect/urls.py
+++ b/fbconnect/urls.py
@@ -1,10 +1,13 @@
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'}, name='xd_receiver'),
+ 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"),
diff --git a/fbconnect/views.py b/fbconnect/views.py
index 5c308e45..1781f6bf 100755
--- a/fbconnect/views.py
+++ b/fbconnect/views.py
@@ -6,7 +6,7 @@ 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 EditUserEmailFeedsForm
+from forum.forms import SimpleEmailSubscribeForm
from django.conf import settings
import fb
@@ -15,9 +15,11 @@ 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:
@@ -26,8 +28,10 @@ def signin(request, newquestion = False, newanswer = False):
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
@@ -35,36 +39,41 @@ def signin(request, newquestion = False, newanswer = False):
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']:
-
- if 'bnewaccount' in request.POST.keys():
+ 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 = EditUserEmailFeedsForm(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 = EditUserEmailFeedsForm()
-
+ email_feeds_form = SimpleEmailSubscribeForm()
+
return render('authopenid/complete.html', {
'form1': form1,
'email_feeds_form': email_feeds_form,
@@ -73,25 +82,31 @@ def register(request, newquestion = False, newanswer = False):
'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 e69de29b..85cd5d26 100644
--- a/forum/__init__.py
+++ b/forum/__init__.py
@@ -0,0 +1 @@
+__all__ = ['admin','auth','const','feed','forms','managers','models','sitemap','urls','views']
diff --git a/forum/admin.py b/forum/admin.py
index 482da048..3afa2241 100644
--- a/forum/admin.py
+++ b/forum/admin.py
@@ -3,7 +3,6 @@
from django.contrib import admin
from models import *
-
class AnonymousQuestionAdmin(admin.ModelAdmin):
"""AnonymousQuestion admin class"""
@@ -46,15 +45,14 @@ class ReputeAdmin(admin.ModelAdmin):
class ActivityAdmin(admin.ModelAdmin):
""" admin class"""
-class BookAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class BookAuthorInfoAdmin(admin.ModelAdmin):
- """ admin class"""
+#class BookAdmin(admin.ModelAdmin):
+# """ admin class"""
-class BookAuthorRssAdmin(admin.ModelAdmin):
- """ admin class"""
+#class BookAuthorInfoAdmin(admin.ModelAdmin):
+# """ admin class"""
+#class BookAuthorRssAdmin(admin.ModelAdmin):
+# """ admin class"""
admin.site.register(Question, QuestionAdmin)
admin.site.register(Tag, TagAdmin)
@@ -69,6 +67,6 @@ admin.site.register(Badge, BadgeAdmin)
admin.site.register(Award, AwardAdmin)
admin.site.register(Repute, ReputeAdmin)
admin.site.register(Activity, ActivityAdmin)
-admin.site.register(Book, BookAdmin)
-admin.site.register(BookAuthorInfo, BookAuthorInfoAdmin)
-admin.site.register(BookAuthorRss, BookAuthorRssAdmin)
+#admin.site.register(Book, BookAdmin)
+#admin.site.register(BookAuthorInfo, BookAuthorInfoAdmin)
+#admin.site.register(BookAuthorRss, BookAuthorRssAdmin)
diff --git a/forum/auth.py b/forum/auth.py
index 1569482f..3533b9ce 100644
--- a/forum/auth.py
+++ b/forum/auth.py
@@ -19,7 +19,7 @@ answer_type = ContentType.objects.get_for_model(Answer)
VOTE_UP = 15
FLAG_OFFENSIVE = 15
POST_IMAGES = 15
-LEAVE_COMMENTS = 50
+LEAVE_COMMENTS = 50
UPLOAD_FILES = 60
VOTE_DOWN = 100
CLOSE_OWN_QUESTIONS = 250
diff --git a/forum/forms.py b/forum/forms.py
index 308f853b..f22763f7 100644
--- a/forum/forms.py
+++ b/forum/forms.py
@@ -4,7 +4,8 @@ from django import forms
from models import *
from const import *
from django.utils.translation import ugettext as _
-from utils.forms import NextUrlField, UserNameField
+from django.contrib.auth.models import User
+from forum.utils.forms import NextUrlField, UserNameField
from recaptcha_django import ReCaptchaField
from django.conf import settings
import logging
diff --git a/forum/forms.py.orig b/forum/forms.py.orig
deleted file mode 100644
index 42becc11..00000000
--- a/forum/forms.py.orig
+++ /dev/null
@@ -1,352 +0,0 @@
-import re
-from datetime import date
-from django import forms
-from models import *
-from const import *
-from django.utils.translation import ugettext as _
-from utils.forms import NextUrlField, UserNameField
-from recaptcha_django import ReCaptchaField
-from django.conf import settings
-import logging
-
-class TitleField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(TitleField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.TextInput(attrs={'size' : 70, 'autocomplete' : 'off'})
- self.max_length = 255
- self.label = _('title')
- self.help_text = _('please enter a descriptive title for your question')
- self.initial = ''
-
- def clean(self, value):
- if len(value) < 10:
- raise forms.ValidationError(_('title must be > 10 characters'))
-
- return value
-
-class EditorField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(EditorField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.Textarea(attrs={'id':'editor'})
- self.label = _('content')
- self.help_text = u''
- self.initial = ''
-
- def clean(self, value):
- if len(value) < 10:
- raise forms.ValidationError(_('question content must be > 10 characters'))
-
- return value
-
-class TagNamesField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(TagNamesField, self).__init__(*args, **kwargs)
- self.required = True
- self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
- self.max_length = 255
- self.label = _('tags')
- #self.help_text = _('please use space to separate tags (this enables autocomplete feature)')
- self.help_text = _('Tags are short keywords, with no spaces within. Up to five tags can be used.')
- self.initial = ''
-
- def clean(self, value):
- value = super(TagNamesField, self).clean(value)
- data = value.strip()
- if len(data) < 1:
- raise forms.ValidationError(_('tags are required'))
-
- split_re = re.compile(r'[ ,]+')
- list = split_re.split(data)
- list_temp = []
- if len(list) > 5:
- raise forms.ValidationError(_('please use 5 tags or less'))
- for tag in list:
- if len(tag) > 20:
- raise forms.ValidationError(_('tags must be shorter than 20 characters'))
- #take tag regex from settings
- tagname_re = re.compile(r'[a-z0-9]+')
- if not tagname_re.match(tag):
- raise forms.ValidationError(_('please use following characters in tags: letters \'a-z\', numbers, and characters \'.-_#\''))
- # only keep one same tag
- if tag not in list_temp and len(tag.strip()) > 0:
- list_temp.append(tag)
- return u' '.join(list_temp)
-
-class WikiField(forms.BooleanField):
- def __init__(self, *args, **kwargs):
- super(WikiField, self).__init__(*args, **kwargs)
- self.required = False
- self.label = _('community wiki')
- self.help_text = _('if you choose community wiki option, the question and answer do not generate points and name of author will not be shown')
- def clean(self,value):
- return value and settings.WIKI_ON
-
-class EmailNotifyField(forms.BooleanField):
- def __init__(self, *args, **kwargs):
- super(EmailNotifyField, self).__init__(*args, **kwargs)
- self.required = False
- self.widget.attrs['class'] = 'nomargin'
-
-class SummaryField(forms.CharField):
- def __init__(self, *args, **kwargs):
- super(SummaryField, self).__init__(*args, **kwargs)
- self.required = False
- self.widget = forms.TextInput(attrs={'size' : 50, 'autocomplete' : 'off'})
- self.max_length = 300
- self.label = _('update summary:')
- self.help_text = _('enter a brief summary of your revision (e.g. fixed spelling, grammar, improved style, this field is optional)')
-
-class ModerateUserForm(forms.ModelForm):
- is_approved = forms.BooleanField(label=_("Automatically accept user's contributions for the email updates"),
- required=False)
-
- def clean_is_approved(self):
- if 'is_approved' not in self.cleaned_data:
- self.cleaned_data['is_approved'] = False
- return self.cleaned_data['is_approved']
-
- class Meta:
- model = User
- fields = ('is_approved',)
-
-class NotARobotForm(forms.Form):
- recaptcha = ReCaptchaField()
-
-class FeedbackForm(forms.Form):
- name = forms.CharField(label=_('Your name:'), required=False)
- email = forms.EmailField(label=_('Email (not shared with anyone):'), required=False)
- message = forms.CharField(label=_('Your message:'), max_length=800,widget=forms.Textarea(attrs={'cols':60}))
- next = NextUrlField()
-
-class AskForm(forms.Form):
- title = TitleField()
- text = EditorField()
- tags = TagNamesField()
- wiki = WikiField()
-
- openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
- user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
-
-class AnswerForm(forms.Form):
- text = EditorField()
- wiki = WikiField()
- openid = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 40, 'class':'openid-input'}))
- user = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email = forms.CharField(required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- email_notify = EmailNotifyField()
- def __init__(self, question, user, *args, **kwargs):
- super(AnswerForm, self).__init__(*args, **kwargs)
- self.fields['email_notify'].widget.attrs['id'] = 'question-subscribe-updates';
- if question.wiki and settings.WIKI_ON:
- self.fields['wiki'].initial = True
- if user.is_authenticated():
- if user in question.followed_by.all():
- self.fields['email_notify'].initial = True
- return
- self.fields['email_notify'].initial = False
-
-
-class CloseForm(forms.Form):
- reason = forms.ChoiceField(choices=CLOSE_REASONS)
-
-class RetagQuestionForm(forms.Form):
- tags = TagNamesField()
- # initialize the default values
- def __init__(self, question, *args, **kwargs):
- super(RetagQuestionForm, self).__init__(*args, **kwargs)
- self.fields['tags'].initial = question.tagnames
-
-class RevisionForm(forms.Form):
- """
- Lists revisions of a Question or Answer
- """
- revision = forms.ChoiceField(widget=forms.Select(attrs={'style' : 'width:520px'}))
-
- def __init__(self, post, latest_revision, *args, **kwargs):
- super(RevisionForm, self).__init__(*args, **kwargs)
- revisions = post.revisions.all().values_list(
- 'revision', 'author__username', 'revised_at', 'summary')
- date_format = '%c'
- self.fields['revision'].choices = [
- (r[0], u'%s - %s (%s) %s' % (r[0], r[1], r[2].strftime(date_format), r[3]))
- for r in revisions]
- self.fields['revision'].initial = latest_revision.revision
-
-class EditQuestionForm(forms.Form):
- title = TitleField()
- text = EditorField()
- tags = TagNamesField()
- summary = SummaryField()
-
- def __init__(self, question, revision, *args, **kwargs):
- super(EditQuestionForm, self).__init__(*args, **kwargs)
- self.fields['title'].initial = revision.title
- self.fields['text'].initial = revision.text
- self.fields['tags'].initial = revision.tagnames
- # Once wiki mode is enabled, it can't be disabled
- if not question.wiki:
- self.fields['wiki'] = WikiField()
-
-class EditAnswerForm(forms.Form):
- text = EditorField()
- summary = SummaryField()
-
- def __init__(self, answer, revision, *args, **kwargs):
- super(EditAnswerForm, self).__init__(*args, **kwargs)
- self.fields['text'].initial = revision.text
-
-class EditUserForm(forms.Form):
- email = forms.EmailField(label=u'Email', help_text=_('this email does not have to be linked to gravatar'), required=True, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- #username = UserNameField(label=_('Screen name'))
- realname = forms.CharField(label=_('Real name'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- website = forms.URLField(label=_('Website'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- city = forms.CharField(label=_('Location'), required=False, max_length=255, widget=forms.TextInput(attrs={'size' : 35}))
- birthday = forms.DateField(label=_('Date of birth'), help_text=_('will not be shown, used to calculate age, format: YYYY-MM-DD'), required=False, widget=forms.TextInput(attrs={'size' : 35}))
- about = forms.CharField(label=_('Profile'), required=False, widget=forms.Textarea(attrs={'cols' : 60}))
-
- def __init__(self, user, *args, **kwargs):
- super(EditUserForm, self).__init__(*args, **kwargs)
- self.fields['username'].initial = user.username
- self.fields['username'].user_instance = user
- self.fields['email'].initial = user.email
- self.fields['realname'].initial = user.real_name
- self.fields['website'].initial = user.website
- self.fields['city'].initial = user.location
-
- if user.date_of_birth is not None:
- self.fields['birthday'].initial = user.date_of_birth
- else:
- self.fields['birthday'].initial = '1990-01-01'
- self.fields['about'].initial = user.about
- self.user = user
-
- def clean_email(self):
- """For security reason one unique email in database"""
- if self.user.email != self.cleaned_data['email']:
- #todo dry it, there is a similar thing in openidauth
- if settings.EMAIL_UNIQUE == True:
- if 'email' in self.cleaned_data:
- try:
- user = User.objects.get(email = self.cleaned_data['email'])
- except User.DoesNotExist:
- return self.cleaned_data['email']
- except User.MultipleObjectsReturned:
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- raise forms.ValidationError(_('this email has already been registered, please use another one'))
- return self.cleaned_data['email']
-
-class TagFilterSelectionForm(forms.ModelForm):
- tag_filter_setting = forms.ChoiceField(choices=TAG_EMAIL_FILTER_CHOICES, #imported from forum/const.py
- initial='ignored',
- label=_('Choose email tag filter'),
- widget=forms.RadioSelect)
- class Meta:
- model = User
- fields = ('tag_filter_setting',)
-
- def save(self):
- before = self.instance.tag_filter_setting
- super(TagFilterSelectionForm, self).save()
- after = self.instance.tag_filter_setting #User.objects.get(pk=self.instance.id).tag_filter_setting
- if before != after:
- return True
- return False
-
-class EditUserEmailFeedsForm(forms.Form):
- WN = (('w',_('weekly')),('n',_('no email')))
- DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
- FORM_TO_MODEL_MAP = {
- 'all_questions':'q_all',
- 'asked_by_me':'q_ask',
- 'answered_by_me':'q_ans',
- 'individually_selected':'q_sel',
- }
- NO_EMAIL_INITIAL = {
- 'all_questions':'n',
- 'asked_by_me':'n',
- 'answered_by_me':'n',
- 'individually_selected':'n',
- }
- asked_by_me = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Asked by me'))
- answered_by_me = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Answered by me'))
- individually_selected = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Individually selected'))
- all_questions = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Entire forum (tag filtered)'),)
-
- def set_initial_values(self,user=None):
- KEY_MAP = dict([(v,k) for k,v in self.FORM_TO_MODEL_MAP.iteritems()])
- if user != None:
- settings = EmailFeedSetting.objects.filter(subscriber=user)
- initial_values = {}
- for setting in settings:
- feed_type = setting.feed_type
- form_field = KEY_MAP[feed_type]
- frequency = setting.frequency
- initial_values[form_field] = frequency
- self.initial = initial_values
- return self
-
- def reset(self):
- self.cleaned_data['all_questions'] = 'n'
- self.cleaned_data['asked_by_me'] = 'n'
- self.cleaned_data['answered_by_me'] = 'n'
- self.cleaned_data['individually_selected'] = 'n'
- self.initial = self.NO_EMAIL_INITIAL
- return self
-
- def save(self,user,save_unbound=False):
- """
- with save_unbound==True will bypass form validation and save initial values
- """
- changed = False
- for form_field, feed_type in self.FORM_TO_MODEL_MAP.items():
- s, created = EmailFeedSetting.objects.get_or_create(subscriber=user,\
- feed_type=feed_type)
- if save_unbound:
- #just save initial values instead
- if form_field in self.initial:
- new_value = self.initial[form_field]
- else:
- new_value = self.fields[form_field].initial
- else:
- new_value = self.cleaned_data[form_field]
- if s.frequency != new_value:
- s.frequency = new_value
- s.save()
- changed = True
- else:
- if created:
- s.save()
- if form_field == 'individually_selected':
- feed_type = ContentType.objects.get_for_model(Question)
- user.followed_questions.clear()
- return changed
-
-
-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 e69de29b..b654caaa 100644
--- a/forum/management/__init__.py
+++ b/forum/management/__init__.py
@@ -0,0 +1,3 @@
+from forum.modules import get_modules_script
+
+get_modules_script('management') \ No newline at end of file
diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py
index 62f13d69..ab318e50 100644
--- a/forum/management/commands/send_email_alerts.py
+++ b/forum/management/commands/send_email_alerts.py
@@ -9,7 +9,7 @@ from django.utils.translation import ungettext
import datetime
from django.conf import settings
import logging
-from utils.odict import OrderedDict
+from forum.utils.odict import OrderedDict
class Command(NoArgsCommand):
def handle_noargs(self,**options):
@@ -140,7 +140,6 @@ class Command(NoArgsCommand):
output.append(_(string) % {'num':number})
def send_email_alerts(self):
-
#todo: move this to template
for user in User.objects.all():
q_list = self.get_updated_questions_for_user(user)
diff --git a/forum/managers.py b/forum/managers.py
deleted file mode 100644
index 3f580bd3..00000000
--- a/forum/managers.py
+++ /dev/null
@@ -1,241 +0,0 @@
-import datetime
-import time
-import logging
-from django.contrib.auth.models import User, UserManager
-from django.db import connection, models, transaction
-from django.db.models import Q
-from forum.models import *
-from urllib import quote, unquote
-
-class QuestionManager(models.Manager):
-
- def update_tags(self, question, tagnames, user):
- """
- Updates Tag associations for a question to match the given
- tagname string.
-
- Returns ``True`` if tag usage counts were updated as a result,
- ``False`` otherwise.
- """
- from forum.models import Tag
- current_tags = list(question.tags.all())
- current_tagnames = set(t.name for t in current_tags)
- updated_tagnames = set(t for t in tagnames.split(' ') if t)
- modified_tags = []
-
- removed_tags = [t for t in current_tags
- if t.name not in updated_tagnames]
- if removed_tags:
- modified_tags.extend(removed_tags)
- question.tags.remove(*removed_tags)
-
- added_tagnames = updated_tagnames - current_tagnames
- if added_tagnames:
- added_tags = Tag.objects.get_or_create_multiple(added_tagnames,
- user)
- modified_tags.extend(added_tags)
- question.tags.add(*added_tags)
-
- if modified_tags:
- Tag.objects.update_use_counts(modified_tags)
- return True
-
- return False
-
- def update_answer_count(self, question):
- """
- Executes an UPDATE query to update denormalised data with the
- number of answers the given question has.
- """
-
- # for some reasons, this Answer class failed to be imported,
- # although we have imported all classes from models on top.
- from forum.models import Answer
- self.filter(id=question.id).update(
- answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count())
-
- def update_view_count(self, question):
- """
- update counter+1 when user browse question page
- """
- self.filter(id=question.id).update(view_count = question.view_count + 1)
-
- def update_favorite_count(self, question):
- """
- update favourite_count for given question
- """
- from forum.models import FavoriteQuestion
- self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count())
-
- def get_similar_questions(self, question):
- """
- Get 10 similar questions for given one.
- This will search the same tag list for give question(by exactly same string) first.
- Questions with the individual tags will be added to list if above questions are not full.
- """
- #print datetime.datetime.now()
- questions = list(self.filter(tagnames = question.tagnames, deleted=False).all())
-
- tags_list = question.tags.all()
- for tag in tags_list:
- extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50]
- for item in extend_questions:
- if item not in questions and len(questions) < 10:
- questions.append(item)
-
- #print datetime.datetime.now()
- return questions
-
-class TagManager(models.Manager):
- UPDATE_USED_COUNTS_QUERY = (
- 'UPDATE tag '
- 'SET used_count = ('
- 'SELECT COUNT(*) FROM question_tags '
- 'INNER JOIN question ON question_id=question.id '
- 'WHERE tag_id = tag.id AND question.deleted=False'
- ') '
- 'WHERE id IN (%s)')
-
- def get_valid_tags(self, page_size):
- from forum.models import Tag
- tags = Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size]
- return tags
-
- def get_or_create_multiple(self, names, user):
- """
- Fetches a list of Tags with the given names, creating any Tags
- which don't exist when necesssary.
- """
- tags = list(self.filter(name__in=names))
- #Set all these tag visible
- for tag in tags:
- if tag.deleted:
- tag.deleted = False
- tag.deleted_by = None
- tag.deleted_at = None
- tag.save()
-
- if len(tags) < len(names):
- existing_names = set(tag.name for tag in tags)
- new_names = [name for name in names if name not in existing_names]
- tags.extend([self.create(name=name, created_by=user)
- for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0])
-
- return tags
-
- def update_use_counts(self, tags):
- """Updates the given Tags with their current use counts."""
- if not tags:
- return
- cursor = connection.cursor()
- query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags))
- cursor.execute(query, [tag.id for tag in tags])
- transaction.commit_unless_managed()
-
- def get_tags_by_questions(self, questions):
- question_ids = []
- for question in questions:
- question_ids.append(question.id)
-
- question_ids_str = ','.join([str(id) for id in question_ids])
- related_tags = self.extra(
- tables=['tag', 'question_tags'],
- where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"]
- ).distinct()
-
- return related_tags
-
-class AnswerManager(models.Manager):
- GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s'
- def get_answers_from_question(self, question, user=None):
- """
- Retrieves visibile answers for the given question. Delete answers
- are only visibile to the person who deleted them.
- """
-
- if user is None or not user.is_authenticated():
- return self.filter(question=question, deleted=False)
- else:
- return self.filter(Q(question=question),
- Q(deleted=False) | Q(deleted_by=user))
-
- def get_answers_from_questions(self, user_id):
- """
- Retrieves visibile answers for the given question. Which are not included own answers
- """
- cursor = connection.cursor()
- cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id])
- return cursor.fetchall()
-
-class VoteManager(models.Manager):
- COUNT_UP_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = 1"
- COUNT_DOWN_VOTE_BY_USER = "SELECT count(*) FROM vote WHERE user_id = %s AND vote = -1"
- COUNT_VOTES_PER_DAY_BY_USER = "SELECT COUNT(*) FROM vote WHERE user_id = %s AND DATE(voted_at) = %s"
- def get_up_vote_count_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_UP_VOTE_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
- else:
- return 0
-
- def get_down_vote_count_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_DOWN_VOTE_BY_USER, [user.id])
- row = cursor.fetchone()
- return row[0]
- else:
- return 0
-
- def get_votes_count_today_from_user(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_VOTES_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-
-class FlaggedItemManager(models.Manager):
- COUNT_FLAGS_PER_DAY_BY_USER = "SELECT COUNT(*) FROM flagged_item WHERE user_id = %s AND DATE(flagged_at) = %s"
- def get_flagged_items_count_today(self, user):
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_FLAGS_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-
-class ReputeManager(models.Manager):
- COUNT_REPUTATION_PER_DAY_BY_USER = "SELECT SUM(positive)+SUM(negative) FROM repute WHERE user_id = %s AND (reputation_type=1 OR reputation_type=-8) AND DATE(reputed_at) = %s"
- def get_reputation_by_upvoted_today(self, user):
- """
- For one user in one day, he can only earn rep till certain score (ep. +200)
- by upvoted(also substracted from upvoted canceled). This is because we need
- to prohibit gaming system by upvoting/cancel again and again.
- """
- if user is not None:
- cursor = connection.cursor()
- cursor.execute(self.COUNT_REPUTATION_PER_DAY_BY_USER, [user.id, time.strftime("%Y-%m-%d", datetime.datetime.now().timetuple())])
- row = cursor.fetchone()
- return row[0]
-
- else:
- return 0
-class AwardManager(models.Manager):
- def get_recent_awards(self):
- awards = super(AwardManager, self).extra(
- select={'badge_id': 'badge.id', 'badge_name':'badge.name',
- 'badge_description': 'badge.description', 'badge_type': 'badge.type',
- 'user_id': 'auth_user.id', 'user_name': 'auth_user.username'
- },
- tables=['award', 'badge', 'auth_user'],
- order_by=['-awarded_at'],
- where=['auth_user.id=award.user_id AND badge_id=badge.id'],
- ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name')
- return awards
diff --git a/middleware/__init__.py b/forum/middleware/__init__.py
index e69de29b..e69de29b 100644
--- a/middleware/__init__.py
+++ b/forum/middleware/__init__.py
diff --git a/middleware/anon_user.py b/forum/middleware/anon_user.py
index fa2686f0..e05e6f33 100644
--- a/middleware/anon_user.py
+++ b/forum/middleware/anon_user.py
@@ -1,7 +1,7 @@
from django.http import HttpResponseRedirect
-from utils.forms import get_next_url
+from forum.utils.forms import get_next_url
from django.utils.translation import ugettext as _
-from user_messages import create_message, get_and_delete_messages
+from forum.user_messages import create_message, get_and_delete_messages
from django.conf import settings
import logging
diff --git a/middleware/cancel.py b/forum/middleware/cancel.py
index 51e1b253..15a4371d 100644
--- a/middleware/cancel.py
+++ b/forum/middleware/cancel.py
@@ -1,5 +1,5 @@
from django.http import HttpResponseRedirect
-from utils.forms import get_next_url
+from forum.utils.forms import get_next_url
import logging
class CancelActionMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
diff --git a/middleware/pagesize.py b/forum/middleware/pagesize.py
index f6e6fcfd..f6e6fcfd 100644
--- a/middleware/pagesize.py
+++ b/forum/middleware/pagesize.py
diff --git a/forum/models.py b/forum/models.py
deleted file mode 100644
index fd9ef760..00000000
--- a/forum/models.py
+++ /dev/null
@@ -1,964 +0,0 @@
-# encoding:utf-8
-import datetime
-import hashlib
-from urllib import quote_plus, urlencode
-from django.db import models, IntegrityError
-from django.utils.http import urlquote as django_urlquote
-from django.utils.html import strip_tags
-from django.core.urlresolvers import reverse
-from django.contrib.auth.models import User
-from django.contrib.contenttypes import generic
-from django.contrib.contenttypes.models import ContentType
-from django.template.defaultfilters import slugify
-from django.db.models.signals import post_delete, post_save, pre_save
-from django.utils.translation import ugettext as _
-from django.utils.safestring import mark_safe
-from django.contrib.sitemaps import ping_google
-import django.dispatch
-from django.conf import settings
-import logging
-import re
-
-if settings.USE_SPHINX_SEARCH == True:
- from djangosphinx.models import SphinxSearch
-
-from forum.managers import *
-from const import *
-
-def get_object_comments(self):
- comments = self.comments.all().order_by('id')
- return comments
-
-def post_get_last_update_info(self):
- when = self.added_at
- who = self.author
- if self.last_edited_at and self.last_edited_at > when:
- when = self.last_edited_at
- who = self.last_edited_by
- comments = self.comments.all()
- if len(comments) > 0:
- for c in comments:
- if c.added_at > when:
- when = c.added_at
- who = c.user
- return when, who
-
-class EmailFeedSetting(models.Model):
- DELTA_TABLE = {
- 'w':datetime.timedelta(7),
- 'd':datetime.timedelta(1),
- 'n':datetime.timedelta(-1),
- }
- FEED_TYPES = (
- ('q_all',_('Entire forum')),
- ('q_ask',_('Questions that I asked')),
- ('q_ans',_('Questions that I answered')),
- ('q_sel',_('Individually selected questions')),
- )
- UPDATE_FREQUENCY = (
- ('w',_('Weekly')),
- ('d',_('Daily')),
- ('n',_('No email')),
- )
- subscriber = models.ForeignKey(User)
- feed_type = models.CharField(max_length=16,choices=FEED_TYPES)
- frequency = models.CharField(max_length=8,choices=UPDATE_FREQUENCY,default='n')
- added_at = models.DateTimeField(auto_now_add=True)
- reported_at = models.DateTimeField(null=True)
-
- def save(self,*args,**kwargs):
- type = self.feed_type
- subscriber = self.subscriber
- similar = self.__class__.objects.filter(feed_type=type,subscriber=subscriber).exclude(pk=self.id)
- if len(similar) > 0:
- raise IntegrityError('email feed setting already exists')
- super(EmailFeedSetting,self).save(*args,**kwargs)
-
-class Tag(models.Model):
- name = models.CharField(max_length=255, unique=True)
- created_by = models.ForeignKey(User, related_name='created_tags')
- deleted = models.BooleanField(default=False)
- deleted_at = models.DateTimeField(null=True, blank=True)
- deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_tags')
- # Denormalised data
- used_count = models.PositiveIntegerField(default=0)
-
- objects = TagManager()
-
- class Meta:
- db_table = u'tag'
- ordering = ('-used_count', 'name')
-
- def __unicode__(self):
- return self.name
-
-class Comment(models.Model):
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- user = models.ForeignKey(User, related_name='comments')
- comment = models.CharField(max_length=300)
- added_at = models.DateTimeField(default=datetime.datetime.now)
-
- class Meta:
- ordering = ('-added_at',)
- db_table = u'comment'
-
- def save(self,**kwargs):
- super(Comment,self).save(**kwargs)
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def __unicode__(self):
- return self.comment
-
-class Vote(models.Model):
- VOTE_UP = +1
- VOTE_DOWN = -1
- VOTE_CHOICES = (
- (VOTE_UP, u'Up'),
- (VOTE_DOWN, u'Down'),
- )
-
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- user = models.ForeignKey(User, related_name='votes')
- vote = models.SmallIntegerField(choices=VOTE_CHOICES)
- voted_at = models.DateTimeField(default=datetime.datetime.now)
-
- objects = VoteManager()
-
- class Meta:
- unique_together = ('content_type', 'object_id', 'user')
- db_table = u'vote'
- def __unicode__(self):
- return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote)
-
- def is_upvote(self):
- return self.vote == self.VOTE_UP
-
- def is_downvote(self):
- return self.vote == self.VOTE_DOWN
-
-class FlaggedItem(models.Model):
- """A flag on a Question or Answer indicating offensive content."""
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- user = models.ForeignKey(User, related_name='flagged_items')
- flagged_at = models.DateTimeField(default=datetime.datetime.now)
-
- objects = FlaggedItemManager()
-
- class Meta:
- unique_together = ('content_type', 'object_id', 'user')
- db_table = u'flagged_item'
- def __unicode__(self):
- return '[%s] flagged at %s' %(self.user, self.flagged_at)
-
-class Question(models.Model):
- title = models.CharField(max_length=300)
- author = models.ForeignKey(User, related_name='questions')
- added_at = models.DateTimeField(default=datetime.datetime.now)
- tags = models.ManyToManyField(Tag, related_name='questions')
- # Status
- wiki = models.BooleanField(default=False)
- wikified_at = models.DateTimeField(null=True, blank=True)
- answer_accepted = models.BooleanField(default=False)
- closed = models.BooleanField(default=False)
- closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions')
- closed_at = models.DateTimeField(null=True, blank=True)
- close_reason = models.SmallIntegerField(choices=CLOSE_REASONS, null=True, blank=True)
- deleted = models.BooleanField(default=False)
- deleted_at = models.DateTimeField(null=True, blank=True)
- deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_questions')
- locked = models.BooleanField(default=False)
- locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_questions')
- locked_at = models.DateTimeField(null=True, blank=True)
- followed_by = models.ManyToManyField(User, related_name='followed_questions')
- # Denormalised data
- score = models.IntegerField(default=0)
- vote_up_count = models.IntegerField(default=0)
- vote_down_count = models.IntegerField(default=0)
- answer_count = models.PositiveIntegerField(default=0)
- comment_count = models.PositiveIntegerField(default=0)
- view_count = models.PositiveIntegerField(default=0)
- offensive_flag_count = models.SmallIntegerField(default=0)
- favourite_count = models.PositiveIntegerField(default=0)
- last_edited_at = models.DateTimeField(null=True, blank=True)
- last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_questions')
- last_activity_at = models.DateTimeField(default=datetime.datetime.now)
- last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions')
- tagnames = models.CharField(max_length=125)
- summary = models.CharField(max_length=180)
- html = models.TextField()
- comments = generic.GenericRelation(Comment)
- votes = generic.GenericRelation(Vote)
- flagged_items = generic.GenericRelation(FlaggedItem)
-
- if settings.USE_SPHINX_SEARCH == True:
- search = SphinxSearch(
- index=' '.join(settings.SPHINX_SEARCH_INDICES),
- mode='SPH_MATCH_ALL',
- )
- logging.debug('have sphinx search')
-
- objects = QuestionManager()
-
- def delete(self):
- super(Question, self).delete()
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def save(self, **kwargs):
- """
- Overridden to manually manage addition of tags when the object
- is first saved.
-
- This is required as we're using ``tagnames`` as the sole means of
- adding and editing tags.
- """
- initial_addition = (self.id is None)
- super(Question, self).save(**kwargs)
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
- if initial_addition:
- tags = Tag.objects.get_or_create_multiple(self.tagname_list(),
- self.author)
- self.tags.add(*tags)
- Tag.objects.update_use_counts(tags)
-
- def tagname_list(self):
- """Creates a list of Tag names from the ``tagnames`` attribute."""
- return [name for name in self.tagnames.split(u' ')]
-
- def tagname_meta_generator(self):
- return u','.join([unicode(tag) for tag in self.tagname_list()])
-
- def get_absolute_url(self):
- return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title)))
-
- def has_favorite_by_user(self, user):
- if not user.is_authenticated():
- return False
- return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0
-
- def get_answer_count_by_user(self, user_id):
- query_set = Answer.objects.filter(author__id=user_id)
- return query_set.filter(question=self).count()
-
- def get_question_title(self):
- if self.closed:
- attr = CONST['closed']
- elif self.deleted:
- attr = CONST['deleted']
- else:
- attr = None
- if attr is not None:
- return u'%s %s' % (self.title, attr)
- else:
- return self.title
-
- def get_revision_url(self):
- return reverse('question_revisions', args=[self.id])
-
- def get_latest_revision(self):
- return self.revisions.all()[0]
-
- get_comments = get_object_comments
-
- def get_last_update_info(self):
-
- when, who = post_get_last_update_info(self)
-
- answers = self.answers.all()
- if len(answers) > 0:
- for a in answers:
- a_when, a_who = a.get_last_update_info()
- if a_when > when:
- when = a_when
- who = a_who
-
- return when, who
-
- def get_update_summary(self,last_reported_at=None,recipient_email=''):
- edited = False
- if self.last_edited_at and self.last_edited_at > last_reported_at:
- if self.last_edited_by.email != recipient_email:
- edited = True
- comments = []
- for comment in self.comments.all():
- if comment.added_at > last_reported_at and comment.user.email != recipient_email:
- comments.append(comment)
- new_answers = []
- answer_comments = []
- modified_answers = []
- commented_answers = []
- import sets
- commented_answers = sets.Set([])
- for answer in self.answers.all():
- if (answer.added_at > last_reported_at and answer.author.email != recipient_email):
- new_answers.append(answer)
- if (answer.last_edited_at
- and answer.last_edited_at > last_reported_at
- and answer.last_edited_by.email != recipient_email):
- modified_answers.append(answer)
- for comment in answer.comments.all():
- if comment.added_at > last_reported_at and comment.user.email != recipient_email:
- commented_answers.add(answer)
- answer_comments.append(comment)
-
- #create the report
- if edited or new_answers or modified_answers or answer_comments:
- out = []
- if edited:
- out.append(_('%(author)s modified the question') % {'author':self.last_edited_by.username})
- if new_answers:
- names = sets.Set(map(lambda x: x.author.username,new_answers))
- people = ', '.join(names)
- out.append(_('%(people)s posted %(new_answer_count)s new answers') \
- % {'new_answer_count':len(new_answers),'people':people})
- if comments:
- names = sets.Set(map(lambda x: x.user.username,comments))
- people = ', '.join(names)
- out.append(_('%(people)s commented the question') % {'people':people})
- if answer_comments:
- names = sets.Set(map(lambda x: x.user.username,answer_comments))
- people = ', '.join(names)
- if len(commented_answers) > 1:
- out.append(_('%(people)s commented answers') % {'people':people})
- else:
- out.append(_('%(people)s commented an answer') % {'people':people})
- url = settings.APP_URL + self.get_absolute_url()
- retval = '<a href="%s">%s</a>:<br>\n' % (url,self.title)
- out = map(lambda x: '<li>' + x + '</li>',out)
- retval += '<ul>' + '\n'.join(out) + '</ul><br>\n'
- return retval
- else:
- return None
-
- def __unicode__(self):
- return self.title
-
- class Meta:
- db_table = u'question'
-
-class QuestionView(models.Model):
- question = models.ForeignKey(Question, related_name='viewed')
- who = models.ForeignKey(User, related_name='question_views')
- when = models.DateTimeField()
-
-class FavoriteQuestion(models.Model):
- """A favorite Question of a User."""
- question = models.ForeignKey(Question)
- user = models.ForeignKey(User, related_name='user_favorite_questions')
- added_at = models.DateTimeField(default=datetime.datetime.now)
- class Meta:
- db_table = u'favorite_question'
- def __unicode__(self):
- return '[%s] favorited at %s' %(self.user, self.added_at)
-
-class MarkedTag(models.Model):
- TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored')))
- tag = models.ForeignKey(Tag, related_name='user_selections')
- user = models.ForeignKey(User, related_name='tag_selections')
- reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS)
-
-class QuestionRevision(models.Model):
- """A revision of a Question."""
- question = models.ForeignKey(Question, related_name='revisions')
- revision = models.PositiveIntegerField(blank=True)
- title = models.CharField(max_length=300)
- author = models.ForeignKey(User, related_name='question_revisions')
- revised_at = models.DateTimeField()
- tagnames = models.CharField(max_length=125)
- summary = models.CharField(max_length=300, blank=True)
- text = models.TextField()
-
- class Meta:
- db_table = u'question_revision'
- ordering = ('-revision',)
-
- def get_question_title(self):
- return self.question.title
-
- def get_absolute_url(self):
- #print 'in QuestionRevision.get_absolute_url()'
- return reverse('question_revisions', args=[self.question.id])
-
- def save(self, **kwargs):
- """Looks up the next available revision number."""
- if not self.revision:
- self.revision = QuestionRevision.objects.filter(
- question=self.question).values_list('revision',
- flat=True)[0] + 1
- super(QuestionRevision, self).save(**kwargs)
-
- def __unicode__(self):
- return u'revision %s of %s' % (self.revision, self.title)
-
-class AnonymousAnswer(models.Model):
- question = models.ForeignKey(Question, related_name='anonymous_answers')
- session_key = models.CharField(max_length=40) #session id for anonymous questions
- wiki = models.BooleanField(default=False)
- added_at = models.DateTimeField(default=datetime.datetime.now)
- ip_addr = models.IPAddressField(max_length=21) #allow high port numbers
- author = models.ForeignKey(User,null=True)
- text = models.TextField()
- summary = models.CharField(max_length=180)
-
- def publish(self,user):
- from forum.views import create_new_answer
- added_at = datetime.datetime.now()
- #print user.id
- create_new_answer(question=self.question,wiki=self.wiki,
- added_at=added_at,text=self.text,
- author=user)
- self.delete()
-
-class AnonymousQuestion(models.Model):
- title = models.CharField(max_length=300)
- session_key = models.CharField(max_length=40) #session id for anonymous questions
- text = models.TextField()
- summary = models.CharField(max_length=180)
- tagnames = models.CharField(max_length=125)
- wiki = models.BooleanField(default=False)
- added_at = models.DateTimeField(default=datetime.datetime.now)
- ip_addr = models.IPAddressField(max_length=21) #allow high port numbers
- author = models.ForeignKey(User,null=True)
-
- def publish(self,user):
- from forum.views import create_new_question
- added_at = datetime.datetime.now()
- create_new_question(title=self.title, author=user, added_at=added_at,
- wiki=self.wiki, tagnames=self.tagnames,
- summary=self.summary, text=self.text)
- self.delete()
-
-class Answer(models.Model):
- question = models.ForeignKey(Question, related_name='answers')
- author = models.ForeignKey(User, related_name='answers')
- added_at = models.DateTimeField(default=datetime.datetime.now)
- # Status
- wiki = models.BooleanField(default=False)
- wikified_at = models.DateTimeField(null=True, blank=True)
- accepted = models.BooleanField(default=False)
- accepted_at = models.DateTimeField(null=True, blank=True)
- deleted = models.BooleanField(default=False)
- deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_answers')
- locked = models.BooleanField(default=False)
- locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_answers')
- locked_at = models.DateTimeField(null=True, blank=True)
- # Denormalised data
- score = models.IntegerField(default=0)
- vote_up_count = models.IntegerField(default=0)
- vote_down_count = models.IntegerField(default=0)
- comment_count = models.PositiveIntegerField(default=0)
- offensive_flag_count = models.SmallIntegerField(default=0)
- last_edited_at = models.DateTimeField(null=True, blank=True)
- last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_answers')
- html = models.TextField()
- comments = generic.GenericRelation(Comment)
- votes = generic.GenericRelation(Vote)
- flagged_items = generic.GenericRelation(FlaggedItem)
-
- objects = AnswerManager()
-
- get_comments = get_object_comments
- get_last_update_info = post_get_last_update_info
-
- def save(self,**kwargs):
- super(Answer,self).save(**kwargs)
- try:
- ping_google()
- except Exception:
- logging.debug('problem pinging google did you register you sitemap with google?')
-
- def get_user_vote(self, user):
- if user.__class__.__name__ == "AnonymousUser":
- return None
-
- votes = self.votes.filter(user=user)
- if votes and votes.count() > 0:
- return votes[0]
- else:
- return None
-
- def get_latest_revision(self):
- return self.revisions.all()[0]
-
- def get_question_title(self):
- return self.question.title
-
- def get_absolute_url(self):
- return '%s%s#%s' % (reverse('question', args=[self.question.id]), django_urlquote(slugify(self.question.title)), self.id)
-
- class Meta:
- db_table = u'answer'
-
- def __unicode__(self):
- return self.html
-
-class AnswerRevision(models.Model):
- """A revision of an Answer."""
- answer = models.ForeignKey(Answer, related_name='revisions')
- revision = models.PositiveIntegerField()
- author = models.ForeignKey(User, related_name='answer_revisions')
- revised_at = models.DateTimeField()
- summary = models.CharField(max_length=300, blank=True)
- text = models.TextField()
-
- def get_absolute_url(self):
- return reverse('answer_revisions', kwargs={'id':self.answer.id})
-
- def get_question_title(self):
- return self.answer.question.title
-
- class Meta:
- db_table = u'answer_revision'
- ordering = ('-revision',)
-
- def save(self, **kwargs):
- """Looks up the next available revision number if not set."""
- if not self.revision:
- self.revision = AnswerRevision.objects.filter(
- answer=self.answer).values_list('revision',
- flat=True)[0] + 1
- super(AnswerRevision, self).save(**kwargs)
-
-class Badge(models.Model):
- """Awarded for notable actions performed on the site by Users."""
- GOLD = 1
- SILVER = 2
- BRONZE = 3
- TYPE_CHOICES = (
- (GOLD, _('gold')),
- (SILVER, _('silver')),
- (BRONZE, _('bronze')),
- )
-
- name = models.CharField(max_length=50)
- type = models.SmallIntegerField(choices=TYPE_CHOICES)
- slug = models.SlugField(max_length=50, blank=True)
- description = models.CharField(max_length=300)
- multiple = models.BooleanField(default=False)
- # Denormalised data
- awarded_count = models.PositiveIntegerField(default=0)
-
- class Meta:
- db_table = u'badge'
- ordering = ('name',)
- unique_together = ('name', 'type')
-
- def __unicode__(self):
- return u'%s: %s' % (self.get_type_display(), self.name)
-
- def save(self, **kwargs):
- if not self.slug:
- self.slug = self.name#slugify(self.name)
- super(Badge, self).save(**kwargs)
-
- def get_absolute_url(self):
- return '%s%s/' % (reverse('badge', args=[self.id]), self.slug)
-
-class Award(models.Model):
- """The awarding of a Badge to a User."""
- user = models.ForeignKey(User, related_name='award_user')
- badge = models.ForeignKey(Badge, related_name='award_badge')
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- awarded_at = models.DateTimeField(default=datetime.datetime.now)
- notified = models.BooleanField(default=False)
- objects = AwardManager()
-
- def __unicode__(self):
- return u'[%s] is awarded a badge [%s] at %s' % (self.user.username, self.badge.name, self.awarded_at)
-
- class Meta:
- db_table = u'award'
-
-class Repute(models.Model):
- """The reputation histories for user"""
- user = models.ForeignKey(User)
- positive = models.SmallIntegerField(default=0)
- negative = models.SmallIntegerField(default=0)
- question = models.ForeignKey(Question)
- reputed_at = models.DateTimeField(default=datetime.datetime.now)
- reputation_type = models.SmallIntegerField(choices=TYPE_REPUTATION)
- reputation = models.IntegerField(default=1)
- objects = ReputeManager()
-
- def __unicode__(self):
- return u'[%s]\' reputation changed at %s' % (self.user.username, self.reputed_at)
-
- class Meta:
- db_table = u'repute'
-
-class Activity(models.Model):
- """
- We keep some history data for user activities
- """
- user = models.ForeignKey(User)
- activity_type = models.SmallIntegerField(choices=TYPE_ACTIVITY)
- active_at = models.DateTimeField(default=datetime.datetime.now)
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
- content_object = generic.GenericForeignKey('content_type', 'object_id')
- is_auditted = models.BooleanField(default=False)
-
- def __unicode__(self):
- return u'[%s] was active at %s' % (self.user.username, self.active_at)
-
- class Meta:
- db_table = u'activity'
-
-class Book(models.Model):
- """
- Model for book info
- """
- user = models.ForeignKey(User)
- title = models.CharField(max_length=255)
- short_name = models.CharField(max_length=255)
- author = models.CharField(max_length=255)
- price = models.DecimalField(max_digits=6, decimal_places=2)
- pages = models.SmallIntegerField()
- published_at = models.DateTimeField()
- publication = models.CharField(max_length=255)
- cover_img = models.CharField(max_length=255)
- tagnames = models.CharField(max_length=125)
- added_at = models.DateTimeField()
- last_edited_at = models.DateTimeField()
- questions = models.ManyToManyField(Question, related_name='book', db_table='book_question')
-
- def get_absolute_url(self):
- return reverse('book', args=[django_urlquote(slugify(self.short_name))])
-
- def __unicode__(self):
- return self.title
- class Meta:
- db_table = u'book'
-
-class BookAuthorInfo(models.Model):
- """
- Model for book author info
- """
- user = models.ForeignKey(User)
- book = models.ForeignKey(Book)
- blog_url = models.CharField(max_length=255)
- added_at = models.DateTimeField()
- last_edited_at = models.DateTimeField()
-
- class Meta:
- db_table = u'book_author_info'
-
-class BookAuthorRss(models.Model):
- """
- Model for book author blog rss
- """
- user = models.ForeignKey(User)
- book = models.ForeignKey(Book)
- title = models.CharField(max_length=255)
- url = models.CharField(max_length=255)
- rss_created_at = models.DateTimeField()
- added_at = models.DateTimeField()
-
- class Meta:
- db_table = u'book_author_rss'
-
-class AnonymousEmail(models.Model):
- #validation key, if used
- key = models.CharField(max_length=32)
- email = models.EmailField(null=False,unique=True)
- isvalid = models.BooleanField(default=False)
-
-# User extend properties
-QUESTIONS_PER_PAGE_CHOICES = (
- (10, u'10'),
- (30, u'30'),
- (50, u'50'),
-)
-
-def user_is_username_taken(cls,username):
- try:
- cls.objects.get(username=username)
- return True
- except cls.MultipleObjectsReturned:
- return True
- except cls.DoesNotExist:
- return False
-
-def user_get_q_sel_email_feed_frequency(self):
- #print 'looking for frequency for user %s' % self
- try:
- feed_setting = EmailFeedSetting.objects.get(subscriber=self,feed_type='q_sel')
- except Exception, e:
- #print 'have error %s' % e.message
- raise e
- #print 'have freq=%s' % feed_setting.frequency
- return feed_setting.frequency
-
-User.add_to_class('is_approved', models.BooleanField(default=False))
-User.add_to_class('email_isvalid', models.BooleanField(default=False))
-User.add_to_class('email_key', models.CharField(max_length=32, null=True))
-User.add_to_class('reputation', models.PositiveIntegerField(default=1))
-User.add_to_class('gravatar', models.CharField(max_length=32))
-User.add_to_class('favorite_questions',
- models.ManyToManyField(Question, through=FavoriteQuestion,
- related_name='favorited_by'))
-User.add_to_class('badges', models.ManyToManyField(Badge, through=Award,
- related_name='awarded_to'))
-User.add_to_class('gold', models.SmallIntegerField(default=0))
-User.add_to_class('silver', models.SmallIntegerField(default=0))
-User.add_to_class('bronze', models.SmallIntegerField(default=0))
-User.add_to_class('questions_per_page',
- models.SmallIntegerField(choices=QUESTIONS_PER_PAGE_CHOICES, default=10))
-User.add_to_class('last_seen',
- models.DateTimeField(default=datetime.datetime.now))
-User.add_to_class('real_name', models.CharField(max_length=100, blank=True))
-User.add_to_class('website', models.URLField(max_length=200, blank=True))
-User.add_to_class('location', models.CharField(max_length=100, blank=True))
-User.add_to_class('date_of_birth', models.DateField(null=True, blank=True))
-User.add_to_class('about', models.TextField(blank=True))
-User.add_to_class('is_username_taken',classmethod(user_is_username_taken))
-User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency)
-User.add_to_class('hide_ignored_questions', models.BooleanField(default=False))
-User.add_to_class('tag_filter_setting',
- models.CharField(
- max_length=16,
- choices=TAG_EMAIL_FILTER_CHOICES,
- default='ignored'
- )
- )
-
-# custom signal
-tags_updated = django.dispatch.Signal(providing_args=["question"])
-edit_question_or_answer = django.dispatch.Signal(providing_args=["instance", "modified_by"])
-delete_post_or_answer = django.dispatch.Signal(providing_args=["instance", "deleted_by"])
-mark_offensive = django.dispatch.Signal(providing_args=["instance", "mark_by"])
-user_updated = django.dispatch.Signal(providing_args=["instance", "updated_by"])
-user_logged_in = django.dispatch.Signal(providing_args=["session"])
-
-
-def get_messages(self):
- messages = []
- for m in self.message_set.all():
- messages.append(m.message)
- return messages
-
-def delete_messages(self):
- self.message_set.all().delete()
-
-def get_profile_url(self):
- """Returns the URL for this User's profile."""
- return '%s%s/' % (reverse('user', args=[self.id]), slugify(self.username))
-
-def get_profile_link(self):
- profile_link = u'<a href="%s">%s</a>' % (self.get_profile_url(),self.username)
- logging.debug('in get profile link %s' % profile_link)
- return mark_safe(profile_link)
-
-User.add_to_class('get_profile_url', get_profile_url)
-User.add_to_class('get_profile_link', get_profile_link)
-User.add_to_class('get_messages', get_messages)
-User.add_to_class('delete_messages', delete_messages)
-
-def calculate_gravatar_hash(instance, **kwargs):
- """Calculates a User's gravatar hash from their email address."""
- if kwargs.get('raw', False):
- return
- instance.gravatar = hashlib.md5(instance.email).hexdigest()
-
-def record_ask_event(instance, created, **kwargs):
- if created:
- activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ASK_QUESTION)
- activity.save()
-
-record_answer_event_re = re.compile("You have received (\d+) .*new response.*")
-def record_answer_event(instance, created, **kwargs):
- if created:
- question_user = instance.question.author
- found_match = False
- for m in question_user.message_set.all():
- match = record_answer_event_re.search(m.message)
- if match:
- found_match = True
- cnt = int(match.group(1))
- m.message = u"You have received %d <a href=\"%s?sort=responses\">new responses</a>." % (cnt+1, get_profile_url(question_user))
- m.save()
- break
- if not found_match:
- question_user.message_set.create(message=u"You have received %d <a href=\"%s?sort=responses\">new response</a>." % (1, get_profile_url(question_user)))
-
- activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ANSWER)
- activity.save()
-
-def record_comment_event(instance, created, **kwargs):
- if created:
- from django.contrib.contenttypes.models import ContentType
- question_type = ContentType.objects.get_for_model(Question)
- question_type_id = question_type.id
- if (instance.content_type_id == question_type_id):
- type = TYPE_ACTIVITY_COMMENT_QUESTION
- else:
- type = TYPE_ACTIVITY_COMMENT_ANSWER
- activity = Activity(user=instance.user, active_at=instance.added_at, content_object=instance, activity_type=type)
- activity.save()
-
-def record_revision_question_event(instance, created, **kwargs):
- if created and instance.revision <> 1:
- activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_QUESTION)
- activity.save()
-
-def record_revision_answer_event(instance, created, **kwargs):
- if created and instance.revision <> 1:
- activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_ANSWER)
- activity.save()
-
-def record_award_event(instance, created, **kwargs):
- """
- After we awarded a badge to user, we need to record this activity and notify user.
- We also recaculate awarded_count of this badge and user information.
- """
- if created:
- activity = Activity(user=instance.user, active_at=instance.awarded_at, content_object=instance,
- activity_type=TYPE_ACTIVITY_PRIZE)
- activity.save()
-
- instance.badge.awarded_count += 1
- instance.badge.save()
-
- if instance.badge.type == Badge.GOLD:
- instance.user.gold += 1
- if instance.badge.type == Badge.SILVER:
- instance.user.silver += 1
- if instance.badge.type == Badge.BRONZE:
- instance.user.bronze += 1
- instance.user.save()
-
-def notify_award_message(instance, created, **kwargs):
- """
- Notify users when they have been awarded badges by using Django message.
- """
- if created:
- user = instance.user
- user.message_set.create(message=u"Congratulations, you have received a badge '%s'. Check out your profile <a href=\"%s\">here</a>." % (instance.badge.name, get_profile_url(user)))
-
-def record_answer_accepted(instance, created, **kwargs):
- """
- when answer is accepted, we record this for question author - who accepted it.
- """
- if not created and instance.accepted:
- activity = Activity(user=instance.question.author, active_at=datetime.datetime.now(), \
- content_object=instance, activity_type=TYPE_ACTIVITY_MARK_ANSWER)
- activity.save()
-
-def update_last_seen(instance, created, **kwargs):
- """
- when user has activities, we update 'last_seen' time stamp for him
- """
- user = instance.user
- user.last_seen = datetime.datetime.now()
- user.save()
-
-def record_vote(instance, created, **kwargs):
- """
- when user have voted
- """
- if created:
- if instance.vote == 1:
- vote_type = TYPE_ACTIVITY_VOTE_UP
- else:
- vote_type = TYPE_ACTIVITY_VOTE_DOWN
-
- activity = Activity(user=instance.user, active_at=instance.voted_at, content_object=instance, activity_type=vote_type)
- activity.save()
-
-def record_cancel_vote(instance, **kwargs):
- """
- when user canceled vote, the vote will be deleted.
- """
- activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_CANCEL_VOTE)
- activity.save()
-
-def record_delete_question(instance, delete_by, **kwargs):
- """
- when user deleted the question
- """
- if instance.__class__ == "Question":
- activity_type = TYPE_ACTIVITY_DELETE_QUESTION
- else:
- activity_type = TYPE_ACTIVITY_DELETE_ANSWER
-
- activity = Activity(user=delete_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=activity_type)
- activity.save()
-
-def record_mark_offensive(instance, mark_by, **kwargs):
- activity = Activity(user=mark_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE)
- activity.save()
-
-def record_update_tags(question, **kwargs):
- """
- when user updated tags of the question
- """
- activity = Activity(user=question.author, active_at=datetime.datetime.now(), content_object=question, activity_type=TYPE_ACTIVITY_UPDATE_TAGS)
- activity.save()
-
-def record_favorite_question(instance, created, **kwargs):
- """
- when user add the question in him favorite questions list.
- """
- if created:
- activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_FAVORITE)
- activity.save()
-
-def record_user_full_updated(instance, **kwargs):
- activity = Activity(user=instance, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED)
- activity.save()
-
-def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs):
- aq_list = AnonymousQuestion.objects.filter(session_key = session_key)
- aa_list = AnonymousAnswer.objects.filter(session_key = session_key)
- import settings
- if settings.EMAIL_VALIDATION == 'on':#add user to the record
- for aq in aq_list:
- aq.author = user
- aq.save()
- for aa in aa_list:
- aa.author = user
- aa.save()
- #maybe add pending posts message?
- else: #just publish the questions
- for aq in aq_list:
- aq.publish(user)
- for aa in aa_list:
- aa.publish(user)
-
-#signal for User modle save changes
-pre_save.connect(calculate_gravatar_hash, sender=User)
-post_save.connect(record_ask_event, sender=Question)
-post_save.connect(record_answer_event, sender=Answer)
-post_save.connect(record_comment_event, sender=Comment)
-post_save.connect(record_revision_question_event, sender=QuestionRevision)
-post_save.connect(record_revision_answer_event, sender=AnswerRevision)
-post_save.connect(record_award_event, sender=Award)
-post_save.connect(notify_award_message, sender=Award)
-post_save.connect(record_answer_accepted, sender=Answer)
-post_save.connect(update_last_seen, sender=Activity)
-post_save.connect(record_vote, sender=Vote)
-post_delete.connect(record_cancel_vote, sender=Vote)
-delete_post_or_answer.connect(record_delete_question, sender=Question)
-delete_post_or_answer.connect(record_delete_question, sender=Answer)
-mark_offensive.connect(record_mark_offensive, sender=Question)
-mark_offensive.connect(record_mark_offensive, sender=Answer)
-tags_updated.connect(record_update_tags, sender=Question)
-post_save.connect(record_favorite_question, sender=FavoriteQuestion)
-user_updated.connect(record_user_full_updated, sender=User)
-user_logged_in.connect(post_stored_anonymous_content)
diff --git a/forum/models/__init__.py b/forum/models/__init__.py
new file mode 100755
index 00000000..9b504103
--- /dev/null
+++ b/forum/models/__init__.py
@@ -0,0 +1,341 @@
+from question import Question ,QuestionRevision, QuestionView, AnonymousQuestion, FavoriteQuestion
+from answer import Answer, AnonymousAnswer, AnswerRevision
+from tag import Tag, MarkedTag
+from meta import Vote, Comment, FlaggedItem
+from user import Activity, AnonymousEmail, EmailFeedSetting
+from repute import Badge, Award, Repute
+
+from base import *
+
+# User extend properties
+QUESTIONS_PER_PAGE_CHOICES = (
+ (10, u'10'),
+ (30, u'30'),
+ (50, u'50'),
+)
+
+def user_is_username_taken(cls,username):
+ try:
+ cls.objects.get(username=username)
+ return True
+ except cls.MultipleObjectsReturned:
+ return True
+ except cls.DoesNotExist:
+ return False
+
+def user_get_q_sel_email_feed_frequency(self):
+ #print 'looking for frequency for user %s' % self
+ try:
+ feed_setting = EmailFeedSetting.objects.get(subscriber=self,feed_type='q_sel')
+ except Exception, e:
+ #print 'have error %s' % e.message
+ raise e
+ #print 'have freq=%s' % feed_setting.frequency
+ return feed_setting.frequency
+
+User.add_to_class('is_approved', models.BooleanField(default=False))
+User.add_to_class('email_isvalid', models.BooleanField(default=False))
+User.add_to_class('email_key', models.CharField(max_length=32, null=True))
+User.add_to_class('reputation', models.PositiveIntegerField(default=1))
+User.add_to_class('gravatar', models.CharField(max_length=32))
+
+#User.add_to_class('favorite_questions',
+# models.ManyToManyField(Question, through=FavoriteQuestion,
+# related_name='favorited_by'))
+
+#User.add_to_class('badges', models.ManyToManyField(Badge, through=Award,
+# related_name='awarded_to'))
+User.add_to_class('gold', models.SmallIntegerField(default=0))
+User.add_to_class('silver', models.SmallIntegerField(default=0))
+User.add_to_class('bronze', models.SmallIntegerField(default=0))
+User.add_to_class('questions_per_page',
+ models.SmallIntegerField(choices=QUESTIONS_PER_PAGE_CHOICES, default=10))
+User.add_to_class('last_seen',
+ models.DateTimeField(default=datetime.datetime.now))
+User.add_to_class('real_name', models.CharField(max_length=100, blank=True))
+User.add_to_class('website', models.URLField(max_length=200, blank=True))
+User.add_to_class('location', models.CharField(max_length=100, blank=True))
+User.add_to_class('date_of_birth', models.DateField(null=True, blank=True))
+User.add_to_class('about', models.TextField(blank=True))
+User.add_to_class('is_username_taken',classmethod(user_is_username_taken))
+User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency)
+User.add_to_class('hide_ignored_questions', models.BooleanField(default=False))
+User.add_to_class('tag_filter_setting',
+ models.CharField(
+ max_length=16,
+ choices=TAG_EMAIL_FILTER_CHOICES,
+ default='ignored'
+ )
+ )
+
+# custom signal
+tags_updated = django.dispatch.Signal(providing_args=["question"])
+edit_question_or_answer = django.dispatch.Signal(providing_args=["instance", "modified_by"])
+delete_post_or_answer = django.dispatch.Signal(providing_args=["instance", "deleted_by"])
+mark_offensive = django.dispatch.Signal(providing_args=["instance", "mark_by"])
+user_updated = django.dispatch.Signal(providing_args=["instance", "updated_by"])
+user_logged_in = django.dispatch.Signal(providing_args=["session"])
+
+
+def get_messages(self):
+ messages = []
+ for m in self.message_set.all():
+ messages.append(m.message)
+ return messages
+
+def delete_messages(self):
+ self.message_set.all().delete()
+
+def get_profile_url(self):
+ """Returns the URL for this User's profile."""
+ return '%s%s/' % (reverse('user', args=[self.id]), slugify(self.username))
+
+def get_profile_link(self):
+ profile_link = u'<a href="%s">%s</a>' % (self.get_profile_url(),self.username)
+ logging.debug('in get profile link %s' % profile_link)
+ return mark_safe(profile_link)
+
+User.add_to_class('get_profile_url', get_profile_url)
+User.add_to_class('get_profile_link', get_profile_link)
+User.add_to_class('get_messages', get_messages)
+User.add_to_class('delete_messages', delete_messages)
+
+def calculate_gravatar_hash(instance, **kwargs):
+ """Calculates a User's gravatar hash from their email address."""
+ if kwargs.get('raw', False):
+ return
+ instance.gravatar = hashlib.md5(instance.email).hexdigest()
+
+def record_ask_event(instance, created, **kwargs):
+ if created:
+ activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ASK_QUESTION)
+ activity.save()
+
+def record_answer_event(instance, created, **kwargs):
+ if created:
+ activity = Activity(user=instance.author, active_at=instance.added_at, content_object=instance, activity_type=TYPE_ACTIVITY_ANSWER)
+ activity.save()
+
+def record_comment_event(instance, created, **kwargs):
+ if created:
+ from django.contrib.contenttypes.models import ContentType
+ question_type = ContentType.objects.get_for_model(Question)
+ question_type_id = question_type.id
+ if (instance.content_type_id == question_type_id):
+ type = TYPE_ACTIVITY_COMMENT_QUESTION
+ else:
+ type = TYPE_ACTIVITY_COMMENT_ANSWER
+ activity = Activity(user=instance.user, active_at=instance.added_at, content_object=instance, activity_type=type)
+ activity.save()
+
+def record_revision_question_event(instance, created, **kwargs):
+ if created and instance.revision <> 1:
+ activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_QUESTION)
+ activity.save()
+
+def record_revision_answer_event(instance, created, **kwargs):
+ if created and instance.revision <> 1:
+ activity = Activity(user=instance.author, active_at=instance.revised_at, content_object=instance, activity_type=TYPE_ACTIVITY_UPDATE_ANSWER)
+ activity.save()
+
+def record_award_event(instance, created, **kwargs):
+ """
+ After we awarded a badge to user, we need to record this activity and notify user.
+ We also recaculate awarded_count of this badge and user information.
+ """
+ if created:
+ activity = Activity(user=instance.user, active_at=instance.awarded_at, content_object=instance,
+ activity_type=TYPE_ACTIVITY_PRIZE)
+ activity.save()
+
+ instance.badge.awarded_count += 1
+ instance.badge.save()
+
+ if instance.badge.type == Badge.GOLD:
+ instance.user.gold += 1
+ if instance.badge.type == Badge.SILVER:
+ instance.user.silver += 1
+ if instance.badge.type == Badge.BRONZE:
+ instance.user.bronze += 1
+ instance.user.save()
+
+def notify_award_message(instance, created, **kwargs):
+ """
+ Notify users when they have been awarded badges by using Django message.
+ """
+ if created:
+ user = instance.user
+ user.message_set.create(message=u"Congratulations, you have received a badge '%s'" % instance.badge.name)
+
+def record_answer_accepted(instance, created, **kwargs):
+ """
+ when answer is accepted, we record this for question author - who accepted it.
+ """
+ if not created and instance.accepted:
+ activity = Activity(user=instance.question.author, active_at=datetime.datetime.now(), \
+ content_object=instance, activity_type=TYPE_ACTIVITY_MARK_ANSWER)
+ activity.save()
+
+def update_last_seen(instance, created, **kwargs):
+ """
+ when user has activities, we update 'last_seen' time stamp for him
+ """
+ user = instance.user
+ user.last_seen = datetime.datetime.now()
+ user.save()
+
+def record_vote(instance, created, **kwargs):
+ """
+ when user have voted
+ """
+ if created:
+ if instance.vote == 1:
+ vote_type = TYPE_ACTIVITY_VOTE_UP
+ else:
+ vote_type = TYPE_ACTIVITY_VOTE_DOWN
+
+ activity = Activity(user=instance.user, active_at=instance.voted_at, content_object=instance, activity_type=vote_type)
+ activity.save()
+
+def record_cancel_vote(instance, **kwargs):
+ """
+ when user canceled vote, the vote will be deleted.
+ """
+ activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_CANCEL_VOTE)
+ activity.save()
+
+def record_delete_question(instance, delete_by, **kwargs):
+ """
+ when user deleted the question
+ """
+ if instance.__class__ == "Question":
+ activity_type = TYPE_ACTIVITY_DELETE_QUESTION
+ else:
+ activity_type = TYPE_ACTIVITY_DELETE_ANSWER
+
+ activity = Activity(user=delete_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=activity_type)
+ activity.save()
+
+def record_mark_offensive(instance, mark_by, **kwargs):
+ activity = Activity(user=mark_by, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_MARK_OFFENSIVE)
+ activity.save()
+
+def record_update_tags(question, **kwargs):
+ """
+ when user updated tags of the question
+ """
+ activity = Activity(user=question.author, active_at=datetime.datetime.now(), content_object=question, activity_type=TYPE_ACTIVITY_UPDATE_TAGS)
+ activity.save()
+
+def record_favorite_question(instance, created, **kwargs):
+ """
+ when user add the question in him favorite questions list.
+ """
+ if created:
+ activity = Activity(user=instance.user, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_FAVORITE)
+ activity.save()
+
+def record_user_full_updated(instance, **kwargs):
+ activity = Activity(user=instance, active_at=datetime.datetime.now(), content_object=instance, activity_type=TYPE_ACTIVITY_USER_FULL_UPDATED)
+ activity.save()
+
+def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs):
+ aq_list = AnonymousQuestion.objects.filter(session_key = session_key)
+ aa_list = AnonymousAnswer.objects.filter(session_key = session_key)
+ import settings
+ if settings.EMAIL_VALIDATION == 'on':#add user to the record
+ for aq in aq_list:
+ aq.author = user
+ aq.save()
+ for aa in aa_list:
+ aa.author = user
+ aa.save()
+ #maybe add pending posts message?
+ else: #just publish the questions
+ for aq in aq_list:
+ aq.publish(user)
+ for aa in aa_list:
+ aa.publish(user)
+
+#signal for User modle save changes
+
+pre_save.connect(calculate_gravatar_hash, sender=User)
+post_save.connect(record_ask_event, sender=Question)
+post_save.connect(record_answer_event, sender=Answer)
+post_save.connect(record_comment_event, sender=Comment)
+post_save.connect(record_revision_question_event, sender=QuestionRevision)
+post_save.connect(record_revision_answer_event, sender=AnswerRevision)
+post_save.connect(record_award_event, sender=Award)
+post_save.connect(notify_award_message, sender=Award)
+post_save.connect(record_answer_accepted, sender=Answer)
+post_save.connect(update_last_seen, sender=Activity)
+post_save.connect(record_vote, sender=Vote)
+post_delete.connect(record_cancel_vote, sender=Vote)
+delete_post_or_answer.connect(record_delete_question, sender=Question)
+delete_post_or_answer.connect(record_delete_question, sender=Answer)
+mark_offensive.connect(record_mark_offensive, sender=Question)
+mark_offensive.connect(record_mark_offensive, sender=Answer)
+tags_updated.connect(record_update_tags, sender=Question)
+post_save.connect(record_favorite_question, sender=FavoriteQuestion)
+user_updated.connect(record_user_full_updated, sender=User)
+user_logged_in.connect(post_stored_anonymous_content)
+
+Question = Question
+QuestionRevision = QuestionRevision
+QuestionView = QuestionView
+FavoriteQuestion = FavoriteQuestion
+AnonymousQuestion = AnonymousQuestion
+
+Answer = Answer
+AnswerRevision = AnswerRevision
+AnonymousAnswer = AnonymousAnswer
+
+Tag = Tag
+Comment = Comment
+Vote = Vote
+FlaggedItem = FlaggedItem
+MarkedTag = MarkedTag
+
+Badge = Badge
+Award = Award
+Repute = Repute
+
+Activity = Activity
+EmailFeedSetting = EmailFeedSetting
+AnonymousEmail = AnonymousEmail
+
+__all__ = [
+ 'Question',
+ 'QuestionRevision',
+ 'QuestionView',
+ 'FavoriteQuestion',
+ 'AnonymousQuestion',
+
+ 'Answer',
+ 'AnswerRevision',
+ 'AnonymousAnswer',
+
+ 'Tag',
+ 'Comment',
+ 'Vote',
+ 'FlaggedItem',
+ 'MarkedTag',
+
+ 'Badge',
+ 'Award',
+ 'Repute',
+
+ 'Activity',
+ 'EmailFeedSetting',
+ 'AnonymousEmail',
+
+ 'User'
+ ]
+
+
+from forum.modules import get_modules_script_classes
+
+for k, v in get_modules_script_classes('models', models.Model).items():
+ if not k in __all__:
+ __all__.append(k)
+ exec "%s = v" % k \ No newline at end of file
diff --git a/forum/models/answer.py b/forum/models/answer.py
new file mode 100755
index 00000000..16e55c69
--- /dev/null
+++ b/forum/models/answer.py
@@ -0,0 +1,133 @@
+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):
+ answer = Answer(
+ question = question,
+ author = author,
+ added_at = added_at,
+ wiki = wiki,
+ html = text
+ )
+ if answer.wiki:
+ answer.last_edited_by = answer.author
+ answer.last_edited_at = added_at
+ answer.wikified_at = added_at
+
+ answer.save()
+
+ #update question data
+ question.last_activity_at = added_at
+ question.last_activity_by = author
+ question.save()
+ Question.objects.update_answer_count(question)
+
+ AnswerRevision.objects.create(
+ answer = answer,
+ revision = 1,
+ author = author,
+ revised_at = added_at,
+ summary = CONST['default_version'],
+ text = text
+ )
+
+ #set notification/delete
+ if email_notify:
+ if author not in question.followed_by.all():
+ question.followed_by.add(author)
+ else:
+ #not sure if this is necessary. ajax should take care of this...
+ try:
+ question.followed_by.remove(author)
+ except:
+ pass
+
+ #GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s'
+ def get_answers_from_question(self, question, user=None):
+ """
+ Retrieves visibile answers for the given question. Delete answers
+ are only visibile to the person who deleted them.
+ """
+
+ if user is None or not user.is_authenticated():
+ return self.filter(question=question, deleted=False)
+ else:
+ return self.filter(models.Q(question=question),
+ models.Q(deleted=False) | models.Q(deleted_by=user))
+
+ #todo: I think this method is not being used anymore, I'll just comment it for now
+ #def get_answers_from_questions(self, user_id):
+ # """
+ # Retrieves visibile answers for the given question. Which are not included own answers
+ # """
+ # cursor = connection.cursor()
+ # cursor.execute(self.GET_ANSWERS_FROM_USER_QUESTIONS, [user_id, user_id])
+ # return cursor.fetchall()
+
+class Answer(Content, DeletableContent):
+ question = models.ForeignKey('Question', related_name='answers')
+ accepted = models.BooleanField(default=False)
+ accepted_at = models.DateTimeField(null=True, blank=True)
+
+ objects = AnswerManager()
+
+ class Meta(Content.Meta):
+ db_table = u'answer'
+
+ def get_user_vote(self, user):
+ if user.__class__.__name__ == "AnonymousUser":
+ return None
+
+ votes = self.votes.filter(user=user)
+ if votes and votes.count() > 0:
+ return votes[0]
+ else:
+ return None
+
+ def get_latest_revision(self):
+ return self.revisions.all()[0]
+
+ def get_question_title(self):
+ return self.question.title
+
+ def get_absolute_url(self):
+ return '%s%s#%s' % (reverse('question', args=[self.question.id]), django_urlquote(slugify(self.question.title)), self.id)
+
+ def __unicode__(self):
+ return self.html
+
+
+class AnswerRevision(ContentRevision):
+ """A revision of an Answer."""
+ answer = models.ForeignKey('Answer', related_name='revisions')
+
+ def get_absolute_url(self):
+ return reverse('answer_revisions', kwargs={'id':self.answer.id})
+
+ def get_question_title(self):
+ return self.answer.question.title
+
+ class Meta(ContentRevision.Meta):
+ db_table = u'answer_revision'
+ ordering = ('-revision',)
+
+ def save(self, **kwargs):
+ """Looks up the next available revision number if not set."""
+ if not self.revision:
+ self.revision = AnswerRevision.objects.filter(
+ answer=self.answer).values_list('revision',
+ flat=True)[0] + 1
+ super(AnswerRevision, self).save(**kwargs)
+
+class AnonymousAnswer(AnonymousContent):
+ question = models.ForeignKey('Question', related_name='anonymous_answers')
+
+ def publish(self,user):
+ added_at = datetime.datetime.now()
+ #print user.id
+ AnswerManager.create_new(question=self.question,wiki=self.wiki,
+ added_at=added_at,text=self.text,
+ author=user)
+ self.delete()
diff --git a/forum/models/base.py b/forum/models/base.py
new file mode 100755
index 00000000..44fa6e66
--- /dev/null
+++ b/forum/models/base.py
@@ -0,0 +1,139 @@
+import datetime
+import hashlib
+from urllib import quote_plus, urlencode
+from django.db import models, IntegrityError, connection, transaction
+from django.utils.http import urlquote as django_urlquote
+from django.utils.html import strip_tags
+from django.core.urlresolvers import reverse
+from django.contrib.auth.models import User
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
+from django.template.defaultfilters import slugify
+from django.db.models.signals import post_delete, post_save, pre_save
+from django.utils.translation import ugettext as _
+from django.utils.safestring import mark_safe
+from django.contrib.sitemaps import ping_google
+import django.dispatch
+from django.conf import settings
+import logging
+
+if settings.USE_SPHINX_SEARCH == True:
+ from djangosphinx.models import SphinxSearch
+
+from forum.const import *
+
+class MetaContent(models.Model):
+ """
+ Base class for Vote, Comment and FlaggedItem
+ """
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+ user = models.ForeignKey(User, related_name='%(class)ss')
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+
+class DeletableContent(models.Model):
+ deleted = models.BooleanField(default=False)
+ deleted_at = models.DateTimeField(null=True, blank=True)
+ deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_%(class)ss')
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+
+class ContentRevision(models.Model):
+ """
+ Base class for QuestionRevision and AnswerRevision
+ """
+ revision = models.PositiveIntegerField()
+ author = models.ForeignKey(User, related_name='%(class)ss')
+ revised_at = models.DateTimeField()
+ summary = models.CharField(max_length=300, blank=True)
+ text = models.TextField()
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+
+class AnonymousContent(models.Model):
+ """
+ Base class for AnonymousQuestion and AnonymousAnswer
+ """
+ session_key = models.CharField(max_length=40) #session id for anonymous questions
+ wiki = models.BooleanField(default=False)
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+ ip_addr = models.IPAddressField(max_length=21) #allow high port numbers
+ author = models.ForeignKey(User,null=True)
+ text = models.TextField()
+ summary = models.CharField(max_length=180)
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+
+from meta import Comment, Vote, FlaggedItem
+
+class Content(models.Model):
+ """
+ Base class for Question and Answer
+ """
+ author = models.ForeignKey(User, related_name='%(class)ss')
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+
+ wiki = models.BooleanField(default=False)
+ wikified_at = models.DateTimeField(null=True, blank=True)
+
+ locked = models.BooleanField(default=False)
+ locked_by = models.ForeignKey(User, null=True, blank=True, related_name='locked_%(class)ss')
+ locked_at = models.DateTimeField(null=True, blank=True)
+
+ score = models.IntegerField(default=0)
+ vote_up_count = models.IntegerField(default=0)
+ vote_down_count = models.IntegerField(default=0)
+
+ comment_count = models.PositiveIntegerField(default=0)
+ offensive_flag_count = models.SmallIntegerField(default=0)
+
+ last_edited_at = models.DateTimeField(null=True, blank=True)
+ last_edited_by = models.ForeignKey(User, null=True, blank=True, related_name='last_edited_%(class)ss')
+
+ html = models.TextField()
+ comments = generic.GenericRelation(Comment)
+ votes = generic.GenericRelation(Vote)
+ flagged_items = generic.GenericRelation(FlaggedItem)
+
+ class Meta:
+ abstract = True
+ app_label = 'forum'
+
+ def save(self,**kwargs):
+ super(Content,self).save(**kwargs)
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+ def get_object_comments(self):
+ comments = self.comments.all().order_by('id')
+ return comments
+
+ def post_get_last_update_info(self):
+ when = self.added_at
+ who = self.author
+ if self.last_edited_at and self.last_edited_at > when:
+ when = self.last_edited_at
+ who = self.last_edited_by
+ comments = self.comments.all()
+ if len(comments) > 0:
+ for c in comments:
+ if c.added_at > when:
+ when = c.added_at
+ who = c.user
+ return when, who \ No newline at end of file
diff --git a/forum/models/meta.py b/forum/models/meta.py
new file mode 100755
index 00000000..7c3f5d36
--- /dev/null
+++ b/forum/models/meta.py
@@ -0,0 +1,89 @@
+from base import *
+
+class VoteManager(models.Manager):
+ def get_up_vote_count_from_user(self, user):
+ if user is not None:
+ return self.filter(user=user, vote=1).count()
+ else:
+ return 0
+
+ def get_down_vote_count_from_user(self, user):
+ if user is not None:
+ return self.filter(user=user, vote=-1).count()
+ else:
+ return 0
+
+ def get_votes_count_today_from_user(self, user):
+ if user is not None:
+ today = datetime.date.today()
+ return self.filter(user=user, voted_at__range=(today, today + datetime.timedelta(1))).count()
+
+ else:
+ return 0
+
+
+class Vote(MetaContent):
+ VOTE_UP = +1
+ VOTE_DOWN = -1
+ VOTE_CHOICES = (
+ (VOTE_UP, u'Up'),
+ (VOTE_DOWN, u'Down'),
+ )
+
+ vote = models.SmallIntegerField(choices=VOTE_CHOICES)
+ voted_at = models.DateTimeField(default=datetime.datetime.now)
+
+ objects = VoteManager()
+
+ class Meta(MetaContent.Meta):
+ unique_together = ('content_type', 'object_id', 'user')
+ db_table = u'vote'
+
+ def __unicode__(self):
+ return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote)
+
+ def is_upvote(self):
+ return self.vote == self.VOTE_UP
+
+ def is_downvote(self):
+ return self.vote == self.VOTE_DOWN
+
+
+class FlaggedItemManager(models.Manager):
+ def get_flagged_items_count_today(self, user):
+ if user is not None:
+ today = datetime.date.today()
+ return self.filter(user=user, flagged_at__range=(today, today + datetime.timedelta(1))).count()
+ else:
+ return 0
+
+class FlaggedItem(MetaContent):
+ """A flag on a Question or Answer indicating offensive content."""
+ flagged_at = models.DateTimeField(default=datetime.datetime.now)
+
+ objects = FlaggedItemManager()
+
+ class Meta(MetaContent.Meta):
+ unique_together = ('content_type', 'object_id', 'user')
+ db_table = u'flagged_item'
+
+ def __unicode__(self):
+ return '[%s] flagged at %s' %(self.user, self.flagged_at)
+
+class Comment(MetaContent):
+ comment = models.CharField(max_length=300)
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+
+ class Meta(MetaContent.Meta):
+ ordering = ('-added_at',)
+ db_table = u'comment'
+
+ def save(self,**kwargs):
+ super(Comment,self).save(**kwargs)
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+ def __unicode__(self):
+ return self.comment \ No newline at end of file
diff --git a/forum/models/question.py b/forum/models/question.py
new file mode 100755
index 00000000..37924a5a
--- /dev/null
+++ b/forum/models/question.py
@@ -0,0 +1,335 @@
+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):
+ question = Question(
+ title = title,
+ author = author,
+ added_at = added_at,
+ last_activity_at = added_at,
+ last_activity_by = author,
+ wiki = wiki,
+ tagnames = tagnames,
+ html = text,
+ summary = summary
+ )
+ if question.wiki:
+ question.last_edited_by = question.author
+ question.last_edited_at = added_at
+ question.wikified_at = added_at
+
+ question.save()
+
+ # create the first revision
+ QuestionRevision.objects.create(
+ question = question,
+ revision = 1,
+ title = question.title,
+ author = author,
+ revised_at = added_at,
+ tagnames = question.tagnames,
+ summary = CONST['default_version'],
+ text = text
+ )
+ return question
+
+ def update_tags(self, question, tagnames, user):
+ """
+ Updates Tag associations for a question to match the given
+ tagname string.
+
+ Returns ``True`` if tag usage counts were updated as a result,
+ ``False`` otherwise.
+ """
+
+ current_tags = list(question.tags.all())
+ current_tagnames = set(t.name for t in current_tags)
+ updated_tagnames = set(t for t in tagnames.split(' ') if t)
+ modified_tags = []
+
+ removed_tags = [t for t in current_tags
+ if t.name not in updated_tagnames]
+ if removed_tags:
+ modified_tags.extend(removed_tags)
+ question.tags.remove(*removed_tags)
+
+ added_tagnames = updated_tagnames - current_tagnames
+ if added_tagnames:
+ added_tags = Tag.objects.get_or_create_multiple(added_tagnames,
+ user)
+ modified_tags.extend(added_tags)
+ question.tags.add(*added_tags)
+
+ if modified_tags:
+ Tag.objects.update_use_counts(modified_tags)
+ return True
+
+ return False
+
+ def update_answer_count(self, question):
+ """
+ Executes an UPDATE query to update denormalised data with the
+ number of answers the given question has.
+ """
+
+ # for some reasons, this Answer class failed to be imported,
+ # although we have imported all classes from models on top.
+ from answer import Answer
+ self.filter(id=question.id).update(
+ answer_count=Answer.objects.get_answers_from_question(question).filter(deleted=False).count())
+
+ def update_view_count(self, question):
+ """
+ update counter+1 when user browse question page
+ """
+ self.filter(id=question.id).update(view_count = question.view_count + 1)
+
+ def update_favorite_count(self, question):
+ """
+ update favourite_count for given question
+ """
+ self.filter(id=question.id).update(favourite_count = FavoriteQuestion.objects.filter(question=question).count())
+
+ def get_similar_questions(self, question):
+ """
+ Get 10 similar questions for given one.
+ This will search the same tag list for give question(by exactly same string) first.
+ Questions with the individual tags will be added to list if above questions are not full.
+ """
+ #print datetime.datetime.now()
+ questions = list(self.filter(tagnames = question.tagnames, deleted=False).all())
+
+ tags_list = question.tags.all()
+ for tag in tags_list:
+ extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50]
+ for item in extend_questions:
+ if item not in questions and len(questions) < 10:
+ questions.append(item)
+
+ #print datetime.datetime.now()
+ return questions
+
+class Question(Content, DeletableContent):
+ title = models.CharField(max_length=300)
+ tags = models.ManyToManyField('Tag', related_name='questions')
+ answer_accepted = models.BooleanField(default=False)
+ closed = models.BooleanField(default=False)
+ closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions')
+ closed_at = models.DateTimeField(null=True, blank=True)
+ close_reason = models.SmallIntegerField(choices=CLOSE_REASONS, null=True, blank=True)
+ followed_by = models.ManyToManyField(User, related_name='followed_questions')
+
+ # Denormalised data
+ answer_count = models.PositiveIntegerField(default=0)
+ view_count = models.PositiveIntegerField(default=0)
+ favourite_count = models.PositiveIntegerField(default=0)
+ last_activity_at = models.DateTimeField(default=datetime.datetime.now)
+ last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions')
+ tagnames = models.CharField(max_length=125)
+ summary = models.CharField(max_length=180)
+
+ favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions')
+
+ objects = QuestionManager()
+
+ class Meta(Content.Meta):
+ db_table = u'question'
+
+ def delete(self):
+ super(Question, self).delete()
+ try:
+ ping_google()
+ except Exception:
+ logging.debug('problem pinging google did you register you sitemap with google?')
+
+ def save(self, **kwargs):
+ """
+ Overridden to manually manage addition of tags when the object
+ is first saved.
+
+ This is required as we're using ``tagnames`` as the sole means of
+ adding and editing tags.
+ """
+ initial_addition = (self.id is None)
+
+ super(Question, self).save(**kwargs)
+
+ if initial_addition:
+ tags = Tag.objects.get_or_create_multiple(self.tagname_list(),
+ self.author)
+ self.tags.add(*tags)
+ Tag.objects.update_use_counts(tags)
+
+ def tagname_list(self):
+ """Creates a list of Tag names from the ``tagnames`` attribute."""
+ return [name for name in self.tagnames.split(u' ')]
+
+ def tagname_meta_generator(self):
+ return u','.join([unicode(tag) for tag in self.tagname_list()])
+
+ def get_absolute_url(self):
+ return '%s%s' % (reverse('question', args=[self.id]), django_urlquote(slugify(self.title)))
+
+ def has_favorite_by_user(self, user):
+ if not user.is_authenticated():
+ return False
+
+ return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0
+
+ def get_answer_count_by_user(self, user_id):
+ from answer import Answer
+ query_set = Answer.objects.filter(author__id=user_id)
+ return query_set.filter(question=self).count()
+
+ def get_question_title(self):
+ if self.closed:
+ attr = CONST['closed']
+ elif self.deleted:
+ attr = CONST['deleted']
+ else:
+ attr = None
+ if attr is not None:
+ return u'%s %s' % (self.title, attr)
+ else:
+ return self.title
+
+ def get_revision_url(self):
+ return reverse('question_revisions', args=[self.id])
+
+ def get_latest_revision(self):
+ return self.revisions.all()[0]
+
+ def get_last_update_info(self):
+ when, who = self.post_get_last_update_info()
+
+ answers = self.answers.all()
+ if len(answers) > 0:
+ for a in answers:
+ a_when, a_who = a.post_get_last_update_info()
+ if a_when > when:
+ when = a_when
+ who = a_who
+
+ return when, who
+
+ def get_update_summary(self,last_reported_at=None,recipient_email=''):
+ edited = False
+ if self.last_edited_at and self.last_edited_at > last_reported_at:
+ if self.last_edited_by.email != recipient_email:
+ edited = True
+ comments = []
+ for comment in self.comments.all():
+ if comment.added_at > last_reported_at and comment.user.email != recipient_email:
+ comments.append(comment)
+ new_answers = []
+ answer_comments = []
+ modified_answers = []
+ commented_answers = []
+ import sets
+ commented_answers = sets.Set([])
+ for answer in self.answers.all():
+ if (answer.added_at > last_reported_at and answer.author.email != recipient_email):
+ new_answers.append(answer)
+ if (answer.last_edited_at
+ and answer.last_edited_at > last_reported_at
+ and answer.last_edited_by.email != recipient_email):
+ modified_answers.append(answer)
+ for comment in answer.comments.all():
+ if comment.added_at > last_reported_at and comment.user.email != recipient_email:
+ commented_answers.add(answer)
+ answer_comments.append(comment)
+
+ #create the report
+ if edited or new_answers or modified_answers or answer_comments:
+ out = []
+ if edited:
+ out.append(_('%(author)s modified the question') % {'author':self.last_edited_by.username})
+ if new_answers:
+ names = sets.Set(map(lambda x: x.author.username,new_answers))
+ people = ', '.join(names)
+ out.append(_('%(people)s posted %(new_answer_count)s new answers') \
+ % {'new_answer_count':len(new_answers),'people':people})
+ if comments:
+ names = sets.Set(map(lambda x: x.user.username,comments))
+ people = ', '.join(names)
+ out.append(_('%(people)s commented the question') % {'people':people})
+ if answer_comments:
+ names = sets.Set(map(lambda x: x.user.username,answer_comments))
+ people = ', '.join(names)
+ if len(commented_answers) > 1:
+ out.append(_('%(people)s commented answers') % {'people':people})
+ else:
+ out.append(_('%(people)s commented an answer') % {'people':people})
+ url = settings.APP_URL + self.get_absolute_url()
+ retval = '<a href="%s">%s</a>:<br>\n' % (url,self.title)
+ out = map(lambda x: '<li>' + x + '</li>',out)
+ retval += '<ul>' + '\n'.join(out) + '</ul><br>\n'
+ return retval
+ else:
+ return None
+
+ def __unicode__(self):
+ return self.title
+
+
+class QuestionView(models.Model):
+ question = models.ForeignKey(Question, related_name='viewed')
+ who = models.ForeignKey(User, related_name='question_views')
+ when = models.DateTimeField()
+
+ class Meta:
+ app_label = 'forum'
+
+class FavoriteQuestion(models.Model):
+ """A favorite Question of a User."""
+ question = models.ForeignKey(Question)
+ user = models.ForeignKey(User, related_name='user_favorite_questions')
+ added_at = models.DateTimeField(default=datetime.datetime.now)
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'favorite_question'
+ def __unicode__(self):
+ return '[%s] favorited at %s' %(self.user, self.added_at)
+
+class QuestionRevision(ContentRevision):
+ """A revision of a Question."""
+ question = models.ForeignKey(Question, related_name='revisions')
+ title = models.CharField(max_length=300)
+ tagnames = models.CharField(max_length=125)
+
+ class Meta(ContentRevision.Meta):
+ db_table = u'question_revision'
+ ordering = ('-revision',)
+
+ def get_question_title(self):
+ return self.question.title
+
+ def get_absolute_url(self):
+ #print 'in QuestionRevision.get_absolute_url()'
+ return reverse('question_revisions', args=[self.question.id])
+
+ def save(self, **kwargs):
+ """Looks up the next available revision number."""
+ if not self.revision:
+ self.revision = QuestionRevision.objects.filter(
+ question=self.question).values_list('revision',
+ flat=True)[0] + 1
+ super(QuestionRevision, self).save(**kwargs)
+
+ def __unicode__(self):
+ return u'revision %s of %s' % (self.revision, self.title)
+
+class AnonymousQuestion(AnonymousContent):
+ title = models.CharField(max_length=300)
+ tagnames = models.CharField(max_length=125)
+
+ def publish(self,user):
+ added_at = datetime.datetime.now()
+ QuestionManager.create_new(title=self.title, author=user, added_at=added_at,
+ wiki=self.wiki, tagnames=self.tagnames,
+ summary=self.summary, text=self.text)
+ self.delete()
+
+from answer import Answer, AnswerManager
diff --git a/forum/models/repute.py b/forum/models/repute.py
new file mode 100755
index 00000000..91ba0375
--- /dev/null
+++ b/forum/models/repute.py
@@ -0,0 +1,109 @@
+from base import *
+
+from django.utils.translation import ugettext as _
+
+class Badge(models.Model):
+ """Awarded for notable actions performed on the site by Users."""
+ GOLD = 1
+ SILVER = 2
+ BRONZE = 3
+ TYPE_CHOICES = (
+ (GOLD, _('gold')),
+ (SILVER, _('silver')),
+ (BRONZE, _('bronze')),
+ )
+
+ name = models.CharField(max_length=50)
+ type = models.SmallIntegerField(choices=TYPE_CHOICES)
+ slug = models.SlugField(max_length=50, blank=True)
+ description = models.CharField(max_length=300)
+ multiple = models.BooleanField(default=False)
+ # Denormalised data
+ awarded_count = models.PositiveIntegerField(default=0)
+
+ awarded_to = models.ManyToManyField(User, through='Award', related_name='badges')
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'badge'
+ ordering = ('name',)
+ unique_together = ('name', 'type')
+
+ def __unicode__(self):
+ return u'%s: %s' % (self.get_type_display(), self.name)
+
+ def save(self, **kwargs):
+ if not self.slug:
+ self.slug = self.name#slugify(self.name)
+ super(Badge, self).save(**kwargs)
+
+ def get_absolute_url(self):
+ return '%s%s/' % (reverse('badge', args=[self.id]), self.slug)
+
+class AwardManager(models.Manager):
+ def get_recent_awards(self):
+ awards = super(AwardManager, self).extra(
+ select={'badge_id': 'badge.id', 'badge_name':'badge.name',
+ 'badge_description': 'badge.description', 'badge_type': 'badge.type',
+ 'user_id': 'auth_user.id', 'user_name': 'auth_user.username'
+ },
+ tables=['award', 'badge', 'auth_user'],
+ order_by=['-awarded_at'],
+ where=['auth_user.id=award.user_id AND badge_id=badge.id'],
+ ).values('badge_id', 'badge_name', 'badge_description', 'badge_type', 'user_id', 'user_name')
+ return awards
+
+class Award(models.Model):
+ """The awarding of a Badge to a User."""
+ user = models.ForeignKey(User, related_name='award_user')
+ badge = models.ForeignKey('Badge', related_name='award_badge')
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+ awarded_at = models.DateTimeField(default=datetime.datetime.now)
+ notified = models.BooleanField(default=False)
+
+ objects = AwardManager()
+
+ def __unicode__(self):
+ return u'[%s] is awarded a badge [%s] at %s' % (self.user.username, self.badge.name, self.awarded_at)
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'award'
+
+class ReputeManager(models.Manager):
+ def get_reputation_by_upvoted_today(self, user):
+ """
+ For one user in one day, he can only earn rep till certain score (ep. +200)
+ by upvoted(also substracted from upvoted canceled). This is because we need
+ to prohibit gaming system by upvoting/cancel again and again.
+ """
+ if user is not None:
+ 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'))
+
+ return sums['positive__sum'] + sums['negative__sum']
+ else:
+ return 0
+
+class Repute(models.Model):
+ """The reputation histories for user"""
+ user = models.ForeignKey(User)
+ positive = models.SmallIntegerField(default=0)
+ negative = models.SmallIntegerField(default=0)
+ question = models.ForeignKey('Question')
+ reputed_at = models.DateTimeField(default=datetime.datetime.now)
+ reputation_type = models.SmallIntegerField(choices=TYPE_REPUTATION)
+ reputation = models.IntegerField(default=1)
+
+ objects = ReputeManager()
+
+ def __unicode__(self):
+ return u'[%s]\' reputation changed at %s' % (self.user.username, self.reputed_at)
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'repute' \ No newline at end of file
diff --git a/forum/models/tag.py b/forum/models/tag.py
new file mode 100755
index 00000000..8d26d6f4
--- /dev/null
+++ b/forum/models/tag.py
@@ -0,0 +1,85 @@
+from base import *
+
+from django.utils.translation import ugettext as _
+
+class TagManager(models.Manager):
+ UPDATE_USED_COUNTS_QUERY = (
+ 'UPDATE tag '
+ 'SET used_count = ('
+ 'SELECT COUNT(*) FROM question_tags '
+ 'INNER JOIN question ON question_id=question.id '
+ 'WHERE tag_id = tag.id AND question.deleted=False'
+ ') '
+ 'WHERE id IN (%s)')
+
+ def get_valid_tags(self, page_size):
+ tags = self.all().filter(deleted=False).exclude(used_count=0).order_by("-id")[:page_size]
+ return tags
+
+ def get_or_create_multiple(self, names, user):
+ """
+ Fetches a list of Tags with the given names, creating any Tags
+ which don't exist when necesssary.
+ """
+ tags = list(self.filter(name__in=names))
+ #Set all these tag visible
+ for tag in tags:
+ if tag.deleted:
+ tag.deleted = False
+ tag.deleted_by = None
+ tag.deleted_at = None
+ tag.save()
+
+ if len(tags) < len(names):
+ existing_names = set(tag.name for tag in tags)
+ new_names = [name for name in names if name not in existing_names]
+ tags.extend([self.create(name=name, created_by=user)
+ for name in new_names if self.filter(name=name).count() == 0 and len(name.strip()) > 0])
+
+ return tags
+
+ def update_use_counts(self, tags):
+ """Updates the given Tags with their current use counts."""
+ if not tags:
+ return
+ cursor = connection.cursor()
+ query = self.UPDATE_USED_COUNTS_QUERY % ','.join(['%s'] * len(tags))
+ cursor.execute(query, [tag.id for tag in tags])
+ transaction.commit_unless_managed()
+
+ def get_tags_by_questions(self, questions):
+ question_ids = []
+ for question in questions:
+ question_ids.append(question.id)
+
+ question_ids_str = ','.join([str(id) for id in question_ids])
+ related_tags = self.extra(
+ tables=['tag', 'question_tags'],
+ where=["tag.id = question_tags.tag_id AND question_tags.question_id IN (" + question_ids_str + ")"]
+ ).distinct()
+
+ return related_tags
+
+class Tag(DeletableContent):
+ name = models.CharField(max_length=255, unique=True)
+ created_by = models.ForeignKey(User, related_name='created_tags')
+ # Denormalised data
+ used_count = models.PositiveIntegerField(default=0)
+
+ objects = TagManager()
+
+ class Meta(DeletableContent.Meta):
+ db_table = u'tag'
+ ordering = ('-used_count', 'name')
+
+ def __unicode__(self):
+ return self.name
+
+class MarkedTag(models.Model):
+ TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored')))
+ tag = models.ForeignKey('Tag', related_name='user_selections')
+ user = models.ForeignKey(User, related_name='tag_selections')
+ reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS)
+
+ class Meta:
+ app_label = 'forum' \ No newline at end of file
diff --git a/forum/models/user.py b/forum/models/user.py
new file mode 100755
index 00000000..ac6cbc0d
--- /dev/null
+++ b/forum/models/user.py
@@ -0,0 +1,67 @@
+from base import *
+
+from django.utils.translation import ugettext as _
+
+class Activity(models.Model):
+ """
+ We keep some history data for user activities
+ """
+ user = models.ForeignKey(User)
+ activity_type = models.SmallIntegerField(choices=TYPE_ACTIVITY)
+ active_at = models.DateTimeField(default=datetime.datetime.now)
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey('content_type', 'object_id')
+ is_auditted = models.BooleanField(default=False)
+
+ def __unicode__(self):
+ return u'[%s] was active at %s' % (self.user.username, self.active_at)
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'activity'
+
+class EmailFeedSetting(models.Model):
+ DELTA_TABLE = {
+ 'w':datetime.timedelta(7),
+ 'd':datetime.timedelta(1),
+ 'n':datetime.timedelta(-1),
+ }
+ FEED_TYPES = (
+ ('q_all',_('Entire forum')),
+ ('q_ask',_('Questions that I asked')),
+ ('q_ans',_('Questions that I answered')),
+ ('q_sel',_('Individually selected questions')),
+ )
+ UPDATE_FREQUENCY = (
+ ('w',_('Weekly')),
+ ('d',_('Daily')),
+ ('n',_('No email')),
+ )
+ subscriber = models.ForeignKey(User)
+ feed_type = models.CharField(max_length=16,choices=FEED_TYPES)
+ frequency = models.CharField(max_length=8,choices=UPDATE_FREQUENCY,default='n')
+ added_at = models.DateTimeField(auto_now_add=True)
+ reported_at = models.DateTimeField(null=True)
+
+ def save(self,*args,**kwargs):
+ type = self.feed_type
+ subscriber = self.subscriber
+ similar = self.__class__.objects.filter(feed_type=type,subscriber=subscriber).exclude(pk=self.id)
+ if len(similar) > 0:
+ raise IntegrityError('email feed setting already exists')
+ super(EmailFeedSetting,self).save(*args,**kwargs)
+
+ class Meta:
+ app_label = 'forum'
+
+class AnonymousEmail(models.Model):
+ #validation key, if used
+ key = models.CharField(max_length=32)
+ email = models.EmailField(null=False,unique=True)
+ isvalid = models.BooleanField(default=False)
+
+ class Meta:
+ app_label = 'forum'
+
+
diff --git a/forum/modules.py b/forum/modules.py
new file mode 100755
index 00000000..26c4d50a
--- /dev/null
+++ b/forum/modules.py
@@ -0,0 +1,54 @@
+import os
+import types
+
+MODULES_PACKAGE = 'forum_modules'
+
+MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../' + MODULES_PACKAGE)
+
+MODULE_LIST = [
+ __import__('forum_modules.%s' % f, globals(), locals(), ['forum_modules'])
+ for f in os.listdir(MODULES_FOLDER)
+ if os.path.isdir(os.path.join(MODULES_FOLDER, f)) and
+ os.path.exists(os.path.join(MODULES_FOLDER, "%s/__init__.py" % f)) and
+ not os.path.exists(os.path.join(MODULES_FOLDER, "%s/DISABLED" % f))
+]
+
+def get_modules_script(script_name):
+ all = []
+
+ for m in MODULE_LIST:
+ try:
+ all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__]))
+ except:
+ pass
+
+ return all
+
+def get_modules_script_classes(script_name, base_class):
+ scripts = get_modules_script(script_name)
+ all_classes = {}
+
+ for script in scripts:
+ all_classes.update(dict([
+ (n, c) for (n, c) in [(n, getattr(script, n)) for n in dir(script)]
+ if isinstance(c, (type, types.ClassType)) and issubclass(c, base_class)
+ ]))
+
+ return all_classes
+
+def get_all_handlers(name):
+ handler_files = get_modules_script('handlers')
+
+ return [
+ h for h in [
+ getattr(f, name) for f in handler_files
+ if hasattr(f, name)
+ ]
+
+ if callable(h)
+ ]
+
+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
diff --git a/forum/skins/README b/forum/skins/README
new file mode 100644
index 00000000..5565fa83
--- /dev/null
+++ b/forum/skins/README
@@ -0,0 +1,22 @@
+this directory contains available skins
+
+1) default - default skin with templates
+2) common - this directory is to media directory common to all or many templates
+
+to create a new skin just create another directory under skins/
+and start populating it with the directory structure as in
+default/templates - templates must be named the same way
+
+NO NEED TO CREATE ALL TEMPLATES/MEDIA FILES AT ONCE
+
+templates are resolved in the following way:
+* check in skin named as in settings.OSQA_DEFAULT_SKIN
+* then skin named 'default'
+
+media is resolved with one extra option
+* settings.OSQA_DEFAULT_SKIN
+* 'default'
+* 'common'
+
+media does not have to be composed of files named the same way as in default skin
+whatever media you link to from your templates - will be in operation
diff --git a/forum/skins/__init__.py b/forum/skins/__init__.py
new file mode 100644
index 00000000..be6bd4f3
--- /dev/null
+++ b/forum/skins/__init__.py
@@ -0,0 +1,57 @@
+from django.conf import settings
+from django.template import loader
+from django.template.loaders import filesystem
+from django.http import HttpResponse
+import os.path
+import logging
+
+#module for skinning osqa
+#at this point skin can be changed only in settings file
+#via OSQA_DEFAULT_SKIN variable
+
+#note - Django template loaders use method django.utils._os.safe_join
+#to work on unicode file paths
+#here it is ignored because it is assumed that we won't use unicode paths
+
+def load_template_source(name, dirs=None):
+ try:
+ tname = os.path.join(settings.OSQA_DEFAULT_SKIN,'templates',name)
+ return filesystem.load_template_source(tname,dirs)
+ except:
+ tname = os.path.join('default','templates',name)
+ return filesystem.load_template_source(tname,dirs)
+load_template_source.is_usable = True
+
+def find_media_source(url):
+ """returns url prefixed with the skin name
+ of the first skin that contains the file
+ directories are searched in this order:
+ settings.OSQA_DEFAULT_SKIN, then 'default', then 'commmon'
+ if file is not found - returns None
+ and logs an error message
+ """
+ while url[0] == '/': url = url[1:]
+ d = os.path.dirname
+ n = os.path.normpath
+ j = os.path.join
+ f = os.path.isfile
+ skins = n(j(d(d(__file__)),'skins'))
+ try:
+ media = os.path.join(skins, settings.OSQA_DEFAULT_SKIN, url)
+ assert(f(media))
+ use_skin = settings.OSQA_DEFAULT_SKIN
+ except:
+ try:
+ media = j(skins, 'default', url)
+ assert(f(media))
+ use_skin = 'default'
+ except:
+ media = j(skins, 'common', url)
+ try:
+ assert(f(media))
+ use_skin = 'common'
+ except:
+ logging.error('could not find media for %s' % url)
+ use_skin = ''
+ return None
+ return use_skin + '/' + url
diff --git a/forum/skins/common/media/README b/forum/skins/common/media/README
new file mode 100644
index 00000000..3376e754
--- /dev/null
+++ b/forum/skins/common/media/README
@@ -0,0 +1 @@
+directory for media common to all or many templates
diff --git a/templates/content/images/blue-up-arrow-h18px.png b/forum/skins/default/media/images/blue-up-arrow-h18px.png
index e1f29e86..e1f29e86 100644
--- a/templates/content/images/blue-up-arrow-h18px.png
+++ b/forum/skins/default/media/images/blue-up-arrow-h18px.png
Binary files differ
diff --git a/templates/content/images/box-arrow.gif b/forum/skins/default/media/images/box-arrow.gif
index 89dcf5b3..89dcf5b3 100644
--- a/templates/content/images/box-arrow.gif
+++ b/forum/skins/default/media/images/box-arrow.gif
Binary files differ
diff --git a/templates/content/images/bullet_green.gif b/forum/skins/default/media/images/bullet_green.gif
index fa530910..fa530910 100644
--- a/templates/content/images/bullet_green.gif
+++ b/forum/skins/default/media/images/bullet_green.gif
Binary files differ
diff --git a/templates/content/images/cc-88x31.png b/forum/skins/default/media/images/cc-88x31.png
index 0f2a0f10..0f2a0f10 100644
--- a/templates/content/images/cc-88x31.png
+++ b/forum/skins/default/media/images/cc-88x31.png
Binary files differ
diff --git a/templates/content/images/cc-wiki.png b/forum/skins/default/media/images/cc-wiki.png
index 3e680538..3e680538 100644
--- a/templates/content/images/cc-wiki.png
+++ b/forum/skins/default/media/images/cc-wiki.png
Binary files differ
diff --git a/templates/content/images/close-small-dark.png b/forum/skins/default/media/images/close-small-dark.png
index 280c1fc7..280c1fc7 100644
--- a/templates/content/images/close-small-dark.png
+++ b/forum/skins/default/media/images/close-small-dark.png
Binary files differ
diff --git a/templates/content/images/close-small-hover.png b/forum/skins/default/media/images/close-small-hover.png
index 7899aec7..7899aec7 100644
--- a/templates/content/images/close-small-hover.png
+++ b/forum/skins/default/media/images/close-small-hover.png
Binary files differ
diff --git a/templates/content/images/close-small.png b/forum/skins/default/media/images/close-small.png
index 5a99d31f..5a99d31f 100644
--- a/templates/content/images/close-small.png
+++ b/forum/skins/default/media/images/close-small.png
Binary files differ
diff --git a/templates/content/images/dash.gif b/forum/skins/default/media/images/dash.gif
index d1ddc507..d1ddc507 100644
--- a/templates/content/images/dash.gif
+++ b/forum/skins/default/media/images/dash.gif
Binary files differ
diff --git a/templates/content/images/djangomade124x25_grey.gif b/forum/skins/default/media/images/djangomade124x25_grey.gif
index d34bb311..d34bb311 100644
--- a/templates/content/images/djangomade124x25_grey.gif
+++ b/forum/skins/default/media/images/djangomade124x25_grey.gif
Binary files differ
diff --git a/templates/content/images/dot-g.gif b/forum/skins/default/media/images/dot-g.gif
index 5d6bb28e..5d6bb28e 100644
--- a/templates/content/images/dot-g.gif
+++ b/forum/skins/default/media/images/dot-g.gif
Binary files differ
diff --git a/templates/content/images/dot-list.gif b/forum/skins/default/media/images/dot-list.gif
index f6a6b865..f6a6b865 100644
--- a/templates/content/images/dot-list.gif
+++ b/forum/skins/default/media/images/dot-list.gif
Binary files differ
diff --git a/templates/content/images/edit.png b/forum/skins/default/media/images/edit.png
index dcb09be0..dcb09be0 100644
--- a/templates/content/images/edit.png
+++ b/forum/skins/default/media/images/edit.png
Binary files differ
diff --git a/templates/content/images/expander-arrow-hide.gif b/forum/skins/default/media/images/expander-arrow-hide.gif
index feb6a618..feb6a618 100644
--- a/templates/content/images/expander-arrow-hide.gif
+++ b/forum/skins/default/media/images/expander-arrow-hide.gif
Binary files differ
diff --git a/templates/content/images/expander-arrow-show.gif b/forum/skins/default/media/images/expander-arrow-show.gif
index 6825c56e..6825c56e 100644
--- a/templates/content/images/expander-arrow-show.gif
+++ b/forum/skins/default/media/images/expander-arrow-show.gif
Binary files differ
diff --git a/templates/content/images/favicon.gif b/forum/skins/default/media/images/favicon.gif
index 910c2666..910c2666 100644
--- a/templates/content/images/favicon.gif
+++ b/forum/skins/default/media/images/favicon.gif
Binary files differ
diff --git a/templates/content/images/feed-icon-small.png b/forum/skins/default/media/images/feed-icon-small.png
index b3c949d2..b3c949d2 100644
--- a/templates/content/images/feed-icon-small.png
+++ b/forum/skins/default/media/images/feed-icon-small.png
Binary files differ
diff --git a/templates/content/images/gray-up-arrow-h18px.png b/forum/skins/default/media/images/gray-up-arrow-h18px.png
index 78767445..78767445 100644
--- a/templates/content/images/gray-up-arrow-h18px.png
+++ b/forum/skins/default/media/images/gray-up-arrow-h18px.png
Binary files differ
diff --git a/templates/content/images/grippie.png b/forum/skins/default/media/images/grippie.png
index 6524d416..6524d416 100644
--- a/templates/content/images/grippie.png
+++ b/forum/skins/default/media/images/grippie.png
Binary files differ
diff --git a/templates/content/images/indicator.gif b/forum/skins/default/media/images/indicator.gif
index 1c72ebb5..1c72ebb5 100644
--- a/templates/content/images/indicator.gif
+++ b/forum/skins/default/media/images/indicator.gif
Binary files differ
diff --git a/templates/content/images/logo.gif b/forum/skins/default/media/images/logo.gif
index ab690de2..ab690de2 100644
--- a/templates/content/images/logo.gif
+++ b/forum/skins/default/media/images/logo.gif
Binary files differ
diff --git a/templates/content/images/logo.png b/forum/skins/default/media/images/logo.png
index 6a250e35..6a250e35 100644
--- a/templates/content/images/logo.png
+++ b/forum/skins/default/media/images/logo.png
Binary files differ
diff --git a/templates/content/images/logo1.png b/forum/skins/default/media/images/logo1.png
index d79a6271..d79a6271 100644
--- a/templates/content/images/logo1.png
+++ b/forum/skins/default/media/images/logo1.png
Binary files differ
diff --git a/templates/content/images/logo2.png b/forum/skins/default/media/images/logo2.png
index bd3cccd9..bd3cccd9 100644
--- a/templates/content/images/logo2.png
+++ b/forum/skins/default/media/images/logo2.png
Binary files differ
diff --git a/templates/content/images/medala.gif b/forum/skins/default/media/images/medala.gif
index 93dd1a39..93dd1a39 100644
--- a/templates/content/images/medala.gif
+++ b/forum/skins/default/media/images/medala.gif
Binary files differ
diff --git a/templates/content/images/medala_on.gif b/forum/skins/default/media/images/medala_on.gif
index a18f9e85..a18f9e85 100644
--- a/templates/content/images/medala_on.gif
+++ b/forum/skins/default/media/images/medala_on.gif
Binary files differ
diff --git a/templates/content/images/new.gif b/forum/skins/default/media/images/new.gif
index 8a220b53..8a220b53 100644
--- a/templates/content/images/new.gif
+++ b/forum/skins/default/media/images/new.gif
Binary files differ
diff --git a/templates/content/images/nophoto.png b/forum/skins/default/media/images/nophoto.png
index 2daf0ffd..2daf0ffd 100644
--- a/templates/content/images/nophoto.png
+++ b/forum/skins/default/media/images/nophoto.png
Binary files differ
diff --git a/templates/content/images/openid.gif b/forum/skins/default/media/images/openid.gif
index 8540e12b..8540e12b 100644
--- a/templates/content/images/openid.gif
+++ b/forum/skins/default/media/images/openid.gif
Binary files differ
diff --git a/templates/content/images/openid/aol.gif b/forum/skins/default/media/images/openid/aol.gif
index decc4f12..decc4f12 100644
--- a/templates/content/images/openid/aol.gif
+++ b/forum/skins/default/media/images/openid/aol.gif
Binary files differ
diff --git a/templates/content/images/openid/blogger.ico b/forum/skins/default/media/images/openid/blogger.ico
index 1b9730b0..1b9730b0 100644
--- a/templates/content/images/openid/blogger.ico
+++ b/forum/skins/default/media/images/openid/blogger.ico
Binary files differ
diff --git a/templates/content/images/openid/claimid.ico b/forum/skins/default/media/images/openid/claimid.ico
index 2b80f491..2b80f491 100644
--- a/templates/content/images/openid/claimid.ico
+++ b/forum/skins/default/media/images/openid/claimid.ico
Binary files differ
diff --git a/templates/content/images/openid/facebook.gif b/forum/skins/default/media/images/openid/facebook.gif
index b997b358..b997b358 100644
--- a/templates/content/images/openid/facebook.gif
+++ b/forum/skins/default/media/images/openid/facebook.gif
Binary files differ
diff --git a/templates/content/images/openid/flickr.ico b/forum/skins/default/media/images/openid/flickr.ico
index 11f6e07f..11f6e07f 100644
--- a/templates/content/images/openid/flickr.ico
+++ b/forum/skins/default/media/images/openid/flickr.ico
Binary files differ
diff --git a/templates/content/images/openid/google.gif b/forum/skins/default/media/images/openid/google.gif
index 1b6cd07b..1b6cd07b 100644
--- a/templates/content/images/openid/google.gif
+++ b/forum/skins/default/media/images/openid/google.gif
Binary files differ
diff --git a/templates/content/images/openid/livejournal.ico b/forum/skins/default/media/images/openid/livejournal.ico
index f3d21ec5..f3d21ec5 100644
--- a/templates/content/images/openid/livejournal.ico
+++ b/forum/skins/default/media/images/openid/livejournal.ico
Binary files differ
diff --git a/templates/content/images/openid/myopenid.ico b/forum/skins/default/media/images/openid/myopenid.ico
index ceb06e6a..ceb06e6a 100644
--- a/templates/content/images/openid/myopenid.ico
+++ b/forum/skins/default/media/images/openid/myopenid.ico
Binary files differ
diff --git a/templates/content/images/openid/openid-inputicon.gif b/forum/skins/default/media/images/openid/openid-inputicon.gif
index cde836c8..cde836c8 100644
--- a/templates/content/images/openid/openid-inputicon.gif
+++ b/forum/skins/default/media/images/openid/openid-inputicon.gif
Binary files differ
diff --git a/templates/content/images/openid/openid.gif b/forum/skins/default/media/images/openid/openid.gif
index c718b0e6..c718b0e6 100644
--- a/templates/content/images/openid/openid.gif
+++ b/forum/skins/default/media/images/openid/openid.gif
Binary files differ
diff --git a/templates/content/images/openid/technorati.ico b/forum/skins/default/media/images/openid/technorati.ico
index fa1083c1..fa1083c1 100644
--- a/templates/content/images/openid/technorati.ico
+++ b/forum/skins/default/media/images/openid/technorati.ico
Binary files differ
diff --git a/templates/content/images/openid/verisign.ico b/forum/skins/default/media/images/openid/verisign.ico
index 3953af93..3953af93 100644
--- a/templates/content/images/openid/verisign.ico
+++ b/forum/skins/default/media/images/openid/verisign.ico
Binary files differ
diff --git a/templates/content/images/openid/vidoop.ico b/forum/skins/default/media/images/openid/vidoop.ico
index bbd9a0d5..bbd9a0d5 100644
--- a/templates/content/images/openid/vidoop.ico
+++ b/forum/skins/default/media/images/openid/vidoop.ico
Binary files differ
diff --git a/templates/content/images/openid/wordpress.ico b/forum/skins/default/media/images/openid/wordpress.ico
index 31b7d2c2..31b7d2c2 100644
--- a/templates/content/images/openid/wordpress.ico
+++ b/forum/skins/default/media/images/openid/wordpress.ico
Binary files differ
diff --git a/templates/content/images/openid/yahoo.gif b/forum/skins/default/media/images/openid/yahoo.gif
index 42adbfa5..42adbfa5 100644
--- a/templates/content/images/openid/yahoo.gif
+++ b/forum/skins/default/media/images/openid/yahoo.gif
Binary files differ
diff --git a/templates/content/images/quest-bg.gif b/forum/skins/default/media/images/quest-bg.gif
index b7540238..b7540238 100644
--- a/templates/content/images/quest-bg.gif
+++ b/forum/skins/default/media/images/quest-bg.gif
Binary files differ
diff --git a/templates/content/images/vote-accepted-on.png b/forum/skins/default/media/images/vote-accepted-on.png
index 2026f3bc..2026f3bc 100644
--- a/templates/content/images/vote-accepted-on.png
+++ b/forum/skins/default/media/images/vote-accepted-on.png
Binary files differ
diff --git a/templates/content/images/vote-accepted.png b/forum/skins/default/media/images/vote-accepted.png
index ecd18551..ecd18551 100644
--- a/templates/content/images/vote-accepted.png
+++ b/forum/skins/default/media/images/vote-accepted.png
Binary files differ
diff --git a/templates/content/images/vote-arrow-down-on.png b/forum/skins/default/media/images/vote-arrow-down-on.png
index 048dbb44..048dbb44 100644
--- a/templates/content/images/vote-arrow-down-on.png
+++ b/forum/skins/default/media/images/vote-arrow-down-on.png
Binary files differ
diff --git a/templates/content/images/vote-arrow-down.png b/forum/skins/default/media/images/vote-arrow-down.png
index e4fdec0a..e4fdec0a 100644
--- a/templates/content/images/vote-arrow-down.png
+++ b/forum/skins/default/media/images/vote-arrow-down.png
Binary files differ
diff --git a/templates/content/images/vote-arrow-up-on.png b/forum/skins/default/media/images/vote-arrow-up-on.png
index 56ad0c25..56ad0c25 100644
--- a/templates/content/images/vote-arrow-up-on.png
+++ b/forum/skins/default/media/images/vote-arrow-up-on.png
Binary files differ
diff --git a/templates/content/images/vote-arrow-up.png b/forum/skins/default/media/images/vote-arrow-up.png
index 6e9a51c7..6e9a51c7 100644
--- a/templates/content/images/vote-arrow-up.png
+++ b/forum/skins/default/media/images/vote-arrow-up.png
Binary files differ
diff --git a/templates/content/images/vote-favorite-off.png b/forum/skins/default/media/images/vote-favorite-off.png
index c1bef074..c1bef074 100644
--- a/templates/content/images/vote-favorite-off.png
+++ b/forum/skins/default/media/images/vote-favorite-off.png
Binary files differ
diff --git a/templates/content/images/vote-favorite-on.png b/forum/skins/default/media/images/vote-favorite-on.png
index 1f9c14ab..1f9c14ab 100644
--- a/templates/content/images/vote-favorite-on.png
+++ b/forum/skins/default/media/images/vote-favorite-on.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/aol.gif b/forum/skins/default/media/jquery-openid/images/aol.gif
index decc4f12..decc4f12 100644
--- a/templates/content/jquery-openid/images/aol.gif
+++ b/forum/skins/default/media/jquery-openid/images/aol.gif
Binary files differ
diff --git a/templates/content/jquery-openid/images/blogger-1.png b/forum/skins/default/media/jquery-openid/images/blogger-1.png
index 8b360ea5..8b360ea5 100644
--- a/templates/content/jquery-openid/images/blogger-1.png
+++ b/forum/skins/default/media/jquery-openid/images/blogger-1.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/blogger.ico b/forum/skins/default/media/jquery-openid/images/blogger.ico
index 1b9730b0..1b9730b0 100644
--- a/templates/content/jquery-openid/images/blogger.ico
+++ b/forum/skins/default/media/jquery-openid/images/blogger.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/claimid-0.png b/forum/skins/default/media/jquery-openid/images/claimid-0.png
index 4a0ea1b3..4a0ea1b3 100644
--- a/templates/content/jquery-openid/images/claimid-0.png
+++ b/forum/skins/default/media/jquery-openid/images/claimid-0.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/claimid.ico b/forum/skins/default/media/jquery-openid/images/claimid.ico
index 2b80f491..2b80f491 100644
--- a/templates/content/jquery-openid/images/claimid.ico
+++ b/forum/skins/default/media/jquery-openid/images/claimid.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/facebook.gif b/forum/skins/default/media/jquery-openid/images/facebook.gif
index b997b358..b997b358 100644
--- a/templates/content/jquery-openid/images/facebook.gif
+++ b/forum/skins/default/media/jquery-openid/images/facebook.gif
Binary files differ
diff --git a/templates/content/jquery-openid/images/flickr.ico b/forum/skins/default/media/jquery-openid/images/flickr.ico
index 11f6e07f..11f6e07f 100644
--- a/templates/content/jquery-openid/images/flickr.ico
+++ b/forum/skins/default/media/jquery-openid/images/flickr.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/flickr.png b/forum/skins/default/media/jquery-openid/images/flickr.png
index 142405a6..142405a6 100644
--- a/templates/content/jquery-openid/images/flickr.png
+++ b/forum/skins/default/media/jquery-openid/images/flickr.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/google.gif b/forum/skins/default/media/jquery-openid/images/google.gif
index 1b6cd07b..1b6cd07b 100644
--- a/templates/content/jquery-openid/images/google.gif
+++ b/forum/skins/default/media/jquery-openid/images/google.gif
Binary files differ
diff --git a/templates/content/jquery-openid/images/livejournal-1.png b/forum/skins/default/media/jquery-openid/images/livejournal-1.png
index e6436081..e6436081 100644
--- a/templates/content/jquery-openid/images/livejournal-1.png
+++ b/forum/skins/default/media/jquery-openid/images/livejournal-1.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/livejournal.ico b/forum/skins/default/media/jquery-openid/images/livejournal.ico
index f3d21ec5..f3d21ec5 100644
--- a/templates/content/jquery-openid/images/livejournal.ico
+++ b/forum/skins/default/media/jquery-openid/images/livejournal.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/myopenid-2.png b/forum/skins/default/media/jquery-openid/images/myopenid-2.png
index f64fb8e8..f64fb8e8 100644
--- a/templates/content/jquery-openid/images/myopenid-2.png
+++ b/forum/skins/default/media/jquery-openid/images/myopenid-2.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/myopenid.ico b/forum/skins/default/media/jquery-openid/images/myopenid.ico
index ceb06e6a..ceb06e6a 100644
--- a/templates/content/jquery-openid/images/myopenid.ico
+++ b/forum/skins/default/media/jquery-openid/images/myopenid.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/openid-inputicon.gif b/forum/skins/default/media/jquery-openid/images/openid-inputicon.gif
index cde836c8..cde836c8 100644
--- a/templates/content/jquery-openid/images/openid-inputicon.gif
+++ b/forum/skins/default/media/jquery-openid/images/openid-inputicon.gif
Binary files differ
diff --git a/templates/content/jquery-openid/images/openid.gif b/forum/skins/default/media/jquery-openid/images/openid.gif
index c718b0e6..c718b0e6 100644
--- a/templates/content/jquery-openid/images/openid.gif
+++ b/forum/skins/default/media/jquery-openid/images/openid.gif
Binary files differ
diff --git a/templates/content/jquery-openid/images/openidico.png b/forum/skins/default/media/jquery-openid/images/openidico.png
index ab622669..ab622669 100644
--- a/templates/content/jquery-openid/images/openidico.png
+++ b/forum/skins/default/media/jquery-openid/images/openidico.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/openidico16.png b/forum/skins/default/media/jquery-openid/images/openidico16.png
index ad718ac5..ad718ac5 100644
--- a/templates/content/jquery-openid/images/openidico16.png
+++ b/forum/skins/default/media/jquery-openid/images/openidico16.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/technorati-1.png b/forum/skins/default/media/jquery-openid/images/technorati-1.png
index f7195240..f7195240 100644
--- a/templates/content/jquery-openid/images/technorati-1.png
+++ b/forum/skins/default/media/jquery-openid/images/technorati-1.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/technorati.ico b/forum/skins/default/media/jquery-openid/images/technorati.ico
index fa1083c1..fa1083c1 100644
--- a/templates/content/jquery-openid/images/technorati.ico
+++ b/forum/skins/default/media/jquery-openid/images/technorati.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/verisign-2.png b/forum/skins/default/media/jquery-openid/images/verisign-2.png
index c1467008..c1467008 100644
--- a/templates/content/jquery-openid/images/verisign-2.png
+++ b/forum/skins/default/media/jquery-openid/images/verisign-2.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/verisign.ico b/forum/skins/default/media/jquery-openid/images/verisign.ico
index 3953af93..3953af93 100644
--- a/templates/content/jquery-openid/images/verisign.ico
+++ b/forum/skins/default/media/jquery-openid/images/verisign.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/vidoop.ico b/forum/skins/default/media/jquery-openid/images/vidoop.ico
index bbd9a0d5..bbd9a0d5 100644
--- a/templates/content/jquery-openid/images/vidoop.ico
+++ b/forum/skins/default/media/jquery-openid/images/vidoop.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/vidoop.png b/forum/skins/default/media/jquery-openid/images/vidoop.png
index 032c9e98..032c9e98 100644
--- a/templates/content/jquery-openid/images/vidoop.png
+++ b/forum/skins/default/media/jquery-openid/images/vidoop.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/wordpress.ico b/forum/skins/default/media/jquery-openid/images/wordpress.ico
index 31b7d2c2..31b7d2c2 100644
--- a/templates/content/jquery-openid/images/wordpress.ico
+++ b/forum/skins/default/media/jquery-openid/images/wordpress.ico
Binary files differ
diff --git a/templates/content/jquery-openid/images/wordpress.png b/forum/skins/default/media/jquery-openid/images/wordpress.png
index ee29f0cf..ee29f0cf 100644
--- a/templates/content/jquery-openid/images/wordpress.png
+++ b/forum/skins/default/media/jquery-openid/images/wordpress.png
Binary files differ
diff --git a/templates/content/jquery-openid/images/yahoo.gif b/forum/skins/default/media/jquery-openid/images/yahoo.gif
index 42adbfa5..42adbfa5 100644
--- a/templates/content/jquery-openid/images/yahoo.gif
+++ b/forum/skins/default/media/jquery-openid/images/yahoo.gif
Binary files differ
diff --git a/templates/content/jquery-openid/jquery.openid.js b/forum/skins/default/media/jquery-openid/jquery.openid.js
index 8d1cd204..8d1cd204 100644
--- a/templates/content/jquery-openid/jquery.openid.js
+++ b/forum/skins/default/media/jquery-openid/jquery.openid.js
diff --git a/templates/content/jquery-openid/openid.css b/forum/skins/default/media/jquery-openid/openid.css
index 1b7aaf82..1b7aaf82 100644
--- a/templates/content/jquery-openid/openid.css
+++ b/forum/skins/default/media/jquery-openid/openid.css
diff --git a/templates/content/js/com.cnprog.admin.js b/forum/skins/default/media/js/com.cnprog.admin.js
index 39dff48c..39dff48c 100644
--- a/templates/content/js/com.cnprog.admin.js
+++ b/forum/skins/default/media/js/com.cnprog.admin.js
diff --git a/templates/content/js/com.cnprog.editor.js b/forum/skins/default/media/js/com.cnprog.editor.js
index 18cc5166..18cc5166 100644
--- a/templates/content/js/com.cnprog.editor.js
+++ b/forum/skins/default/media/js/com.cnprog.editor.js
diff --git a/templates/content/js/com.cnprog.i18n.js b/forum/skins/default/media/js/com.cnprog.i18n.js
index da9bf396..da9bf396 100644
--- a/templates/content/js/com.cnprog.i18n.js
+++ b/forum/skins/default/media/js/com.cnprog.i18n.js
diff --git a/templates/content/js/com.cnprog.post.js b/forum/skins/default/media/js/com.cnprog.post.js
index 668c80fe..4325e665 100644
--- a/templates/content/js/com.cnprog.post.js
+++ b/forum/skins/default/media/js/com.cnprog.post.js
@@ -153,17 +153,17 @@ var Vote = function(){
var setVoteImage = function(voteType, undo, object){
var flag = undo ? "" : "-on";
var arrow = (voteType == VoteType.questionUpVote || voteType == VoteType.answerUpVote) ? "up" : "down";
- object.attr("src", scriptUrl + "content/images/vote-arrow-"+ arrow + flag +".png");
+ object.attr("src", mediaUrl("media/images/vote-arrow-"+ arrow + flag +".png"));
// if undo voting, then undo the pair of arrows.
if(undo){
if(voteType == VoteType.questionUpVote || voteType == VoteType.questionDownVote){
- $(getQuestionVoteUpButton()).attr("src", scriptUrl + "content/images/vote-arrow-up.png");
- $(getQuestionVoteDownButton()).attr("src", scriptUrl + "content/images/vote-arrow-down.png");
+ $(getQuestionVoteUpButton()).attr("src", mediaUrl("media/images/vote-arrow-up.png"));
+ $(getQuestionVoteDownButton()).attr("src", mediaUrl("media/images/vote-arrow-down.png"));
}
else{
- $(getAnswerVoteUpButton(postId)).attr("src", scriptUrl + "content/images/vote-arrow-up.png");
- $(getAnswerVoteDownButton(postId)).attr("src", scriptUrl + "content/images/vote-arrow-down.png");
+ $(getAnswerVoteUpButton(postId)).attr("src", mediaUrl("media/images/vote-arrow-up.png"));
+ $(getAnswerVoteDownButton(postId)).attr("src", mediaUrl("media/images/vote-arrow-down.png"));
}
}
};
@@ -259,19 +259,19 @@ var Vote = function(){
showMessage(object, acceptOwnAnswerMessage);
}
else if(data.status == "1"){
- object.attr("src", scriptUrl + "content/images/vote-accepted.png");
+ object.attr("src", mediaUrl("media/images/vote-accepted.png"));
$("#"+answerContainerIdPrefix+postId).removeClass("accepted-answer");
$("#"+commentLinkIdPrefix+postId).removeClass("comment-link-accepted");
}
else if(data.success == "1"){
var acceptedButtons = 'div.'+ voteContainerId +' img[id^='+ imgIdPrefixAccept +']';
- $(acceptedButtons).attr("src", scriptUrl + "content/images/vote-accepted.png");
+ $(acceptedButtons).attr("src", mediaUrl("media/images/vote-accepted.png"));
var answers = ("div[id^="+answerContainerIdPrefix +"]");
$(answers).removeClass("accepted-answer");
var commentLinks = ("div[id^="+answerContainerIdPrefix +"] div[id^="+ commentLinkIdPrefix +"]");
$(commentLinks).removeClass("comment-link-accepted");
- object.attr("src", scriptUrl + "content/images/vote-accepted-on.png");
+ object.attr("src", mediaUrl("media/images/vote-accepted-on.png"));
$("#"+answerContainerIdPrefix+postId).addClass("accepted-answer");
$("#"+commentLinkIdPrefix+postId).addClass("comment-link-accepted");
}
@@ -285,7 +285,7 @@ var Vote = function(){
showMessage(object, favoriteAnonymousMessage.replace("{{QuestionID}}", questionId));
}
else if(data.status == "1"){
- object.attr("src", scriptUrl + "content/images/vote-favorite-off.png");
+ object.attr("src", mediaUrl("media/images/vote-favorite-off.png"));
var fav = getFavoriteNumber();
fav.removeClass("my-favorite-number");
if(data.count === 0){
@@ -294,7 +294,7 @@ var Vote = function(){
fav.text(data.count);
}
else if(data.success == "1"){
- object.attr("src", scriptUrl + "content/images/vote-favorite-on.png");
+ object.attr("src", mediaUrl("media/images/vote-favorite-on.png"));
var fav = getFavoriteNumber();
fav.text(data.count);
fav.addClass("my-favorite-number");
@@ -538,8 +538,8 @@ function createComments(type) {
var renderDeleteCommentIcon = function(post_id, delete_url){
if (canPostComments(post_id)){
var html = '';
- var img = scriptUrl + "content/images/close-small.png";
- var imgHover = scriptUrl + "content/images/close-small-hover.png";
+ var img = mediaUrl("media/images/close-small.png");
+ var imgHover = mediaUrl("media/images/close-small-hover.png");
html += '<img class="delete-icon" onclick="' + objectType + 'Comments.deleteComment($(this), ' + post_id + ', \'' + delete_url + '\')" src="' + img;
html += '" onmouseover="$(this).attr(\'src\', \'' + imgHover + '\')" onmouseout="$(this).attr(\'src\', \'' + img;
html += '\')" title="' + $.i18n._('delete this comment') + '" />';
@@ -624,12 +624,12 @@ function createComments(type) {
delete_icon.click(function(){CommentsClass.deleteComment($(this),comment_id,delete_url);});
delete_icon.unbind('mouseover').bind('mouseover',
function(){
- $(this).attr('src',scriptUrl + 'content/images/close-small-hover.png');
+ $(this).attr('src',mediaUrl('media/images/close-small-hover.png'));
}
);
delete_icon.unbind('mouseout').bind('mouseout',
function(){
- $(this).attr('src',scriptUrl + 'content/images/close-small.png');
+ $(this).attr('src',mediaUrl('media/images/close-small.png'));
}
);
}
diff --git a/templates/content/js/com.cnprog.tag_selector.js b/forum/skins/default/media/js/com.cnprog.tag_selector.js
index 06aefcfc..e3279e65 100644
--- a/templates/content/js/com.cnprog.tag_selector.js
+++ b/forum/skins/default/media/js/com.cnprog.tag_selector.js
@@ -45,10 +45,10 @@ function pickedTags(){
var setupTagDeleteEvents = function(obj,tag_store,tagname,reason,send_ajax){
obj.unbind('mouseover').bind('mouseover', function(){
- $(this).attr('src', scriptUrl + 'content/images/close-small-hover.png');
+ $(this).attr('src', mediaUrl('media/images/close-small-hover.png'));
});
obj.unbind('mouseout').bind('mouseout', function(){
- $(this).attr('src', scriptUrl + 'content/images/close-small-dark.png');
+ $(this).attr('src', mediaUrl('media/images/close-small-dark.png'));
});
obj.click( function(){
unpickTag(tag_store,tagname,reason,send_ajax);
@@ -88,7 +88,7 @@ function pickedTags(){
tag_link.html(tagname);
var del_link = $('<img></img>');
del_link.addClass('delete-icon');
- del_link.attr('src', scriptUrl + 'content/images/close-small-dark.png');
+ del_link.attr('src', mediaUrl('/media/images/close-small-dark.png'));
setupTagDeleteEvents(del_link, to_target, tagname, reason, true);
diff --git a/templates/content/js/com.cnprog.utils.js b/forum/skins/default/media/js/com.cnprog.utils.js
index 4c3aafba..3f7720c1 100644
--- a/templates/content/js/com.cnprog.utils.js
+++ b/forum/skins/default/media/js/com.cnprog.utils.js
@@ -1,7 +1,11 @@
-//var $, scriptUrl;
+//var $, scriptUrl, osqaSkin
+var mediaUrl = function(resource){
+ return scriptUrl + 'm/' + osqaSkin + '/' + resource;
+};
+
var showMessage = function(object, msg) {
var div = $('<div class="vote-notification"><h3>' + msg + '</h3>(' +
- $.i18n._('click to close') + ')</div>');
+ $.i18n._('click to close') + ')</div>');
div.click(function(event) {
$(".vote-notification").fadeOut("fast", function() { $(this).remove(); });
@@ -37,11 +41,11 @@ var notify = function() {
function appendLoader(containerSelector) {
$(containerSelector).append('<img class="ajax-loader" ' +
- 'src="' + scriptUrl + 'content/images/indicator.gif" title="' +
- $.i18n._('loading...') +
- '" alt="' +
- $.i18n._('loading...') +
- '" />');
+ 'src="' + mediaUrl("media/images/indicator.gif") + '" title="' +
+ $.i18n._('loading...') +
+ '" alt="' +
+ $.i18n._('loading...') +
+ '" />');
}
function removeLoader() {
diff --git a/templates/content/js/compress.bat b/forum/skins/default/media/js/compress.bat
index 5b2673cf..5b2673cf 100644
--- a/templates/content/js/compress.bat
+++ b/forum/skins/default/media/js/compress.bat
diff --git a/templates/content/js/excanvas.pack.js b/forum/skins/default/media/js/excanvas.pack.js
index 71d6fbd9..71d6fbd9 100644
--- a/templates/content/js/excanvas.pack.js
+++ b/forum/skins/default/media/js/excanvas.pack.js
diff --git a/templates/content/js/flot-build.bat b/forum/skins/default/media/js/flot-build.bat
index f9f32cb7..f9f32cb7 100644
--- a/templates/content/js/flot-build.bat
+++ b/forum/skins/default/media/js/flot-build.bat
diff --git a/templates/content/js/jquery-1.2.6.js b/forum/skins/default/media/js/jquery-1.2.6.js
index 88e661ee..88e661ee 100644
--- a/templates/content/js/jquery-1.2.6.js
+++ b/forum/skins/default/media/js/jquery-1.2.6.js
diff --git a/templates/content/js/jquery-1.2.6.min.js b/forum/skins/default/media/js/jquery-1.2.6.min.js
index 82b98e1d..82b98e1d 100644
--- a/templates/content/js/jquery-1.2.6.min.js
+++ b/forum/skins/default/media/js/jquery-1.2.6.min.js
diff --git a/templates/content/js/jquery.ajaxfileupload.js b/forum/skins/default/media/js/jquery.ajaxfileupload.js
index 75292776..75292776 100644
--- a/templates/content/js/jquery.ajaxfileupload.js
+++ b/forum/skins/default/media/js/jquery.ajaxfileupload.js
diff --git a/templates/content/js/jquery.flot.js b/forum/skins/default/media/js/jquery.flot.js
index 83b61929..83b61929 100644
--- a/templates/content/js/jquery.flot.js
+++ b/forum/skins/default/media/js/jquery.flot.js
diff --git a/templates/content/js/jquery.flot.pack.js b/forum/skins/default/media/js/jquery.flot.pack.js
index a5714f12..a5714f12 100644
--- a/templates/content/js/jquery.flot.pack.js
+++ b/forum/skins/default/media/js/jquery.flot.pack.js
diff --git a/templates/content/js/jquery.form.js b/forum/skins/default/media/js/jquery.form.js
index 443114fd..443114fd 100644
--- a/templates/content/js/jquery.form.js
+++ b/forum/skins/default/media/js/jquery.form.js
diff --git a/templates/content/js/jquery.i18n.js b/forum/skins/default/media/js/jquery.i18n.js
index 0a155a31..0a155a31 100644
--- a/templates/content/js/jquery.i18n.js
+++ b/forum/skins/default/media/js/jquery.i18n.js
diff --git a/templates/content/js/jquery.openid.js b/forum/skins/default/media/js/jquery.openid.js
index 17e58e04..af7d8cb9 100644
--- a/templates/content/js/jquery.openid.js
+++ b/forum/skins/default/media/js/jquery.openid.js
@@ -81,7 +81,7 @@ var openid = {
cookie_name: 'openid_provider',
cookie_path: '/',
- img_path: '/content/images/openid/',
+ img_path: '/media/images/openid/',
input_id: null,
provider_url: null,
diff --git a/templates/content/js/jquery.validate.pack.js b/forum/skins/default/media/js/jquery.validate.pack.js
index 49134500..49134500 100644
--- a/templates/content/js/jquery.validate.pack.js
+++ b/forum/skins/default/media/js/jquery.validate.pack.js
diff --git a/templates/content/js/se_hilite.js b/forum/skins/default/media/js/se_hilite.js
index 42e99c8e..42e99c8e 100644
--- a/templates/content/js/se_hilite.js
+++ b/forum/skins/default/media/js/se_hilite.js
diff --git a/templates/content/js/se_hilite_src.js b/forum/skins/default/media/js/se_hilite_src.js
index b604f156..b604f156 100644
--- a/templates/content/js/se_hilite_src.js
+++ b/forum/skins/default/media/js/se_hilite_src.js
diff --git a/templates/content/js/wmd/images/wmd-buttons.png b/forum/skins/default/media/js/wmd/images/wmd-buttons.png
index 50b37090..50b37090 100644
--- a/templates/content/js/wmd/images/wmd-buttons.png
+++ b/forum/skins/default/media/js/wmd/images/wmd-buttons.png
Binary files differ
diff --git a/templates/content/js/wmd/showdown-min.js b/forum/skins/default/media/js/wmd/showdown-min.js
index 073613b1..073613b1 100644
--- a/templates/content/js/wmd/showdown-min.js
+++ b/forum/skins/default/media/js/wmd/showdown-min.js
diff --git a/templates/content/js/wmd/showdown.js b/forum/skins/default/media/js/wmd/showdown.js
index 3f4b9947..3f4b9947 100644
--- a/templates/content/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
new file mode 100644
index 00000000..aa643f1a
--- /dev/null
+++ b/forum/skins/default/media/js/wmd/wmd-min.js
@@ -0,0 +1 @@
+var Attacklab=Attacklab||{};Attacklab.wmdBase=function(){var y=top.Attacklab;var E=top.document;var s=top.RegExp;var l=top.navigator;y.Util={};y.Position={};y.Command={};y.Global={};var a=y.Util;var C=y.Position;var h=y.Command;var v=y.Global;v.isIE=/msie/.test(l.userAgent.toLowerCase());v.isIE_5or6=/msie 6/.test(l.userAgent.toLowerCase())||/msie 5/.test(l.userAgent.toLowerCase());v.isIE_7plus=v.isIE&&!v.isIE_5or6;v.isOpera=/opera/.test(l.userAgent.toLowerCase());v.isKonqueror=/konqueror/.test(l.userAgent.toLowerCase());var c="粗体 <strong> Ctrl-B";var f="斜体 <em> Ctrl-I";var z="超链接 <a> Ctrl-L";var u="引用 <blockquote> Ctrl-.";var e="代码 <pre><code> Ctrl-K";var d="图片 <img> Ctrl-G";var q="数字编号列表 <ol> Ctrl-O";var t="项目符号列表 <ul> Ctrl-U";var i="标题 <h1>/<h2> Ctrl-H";var p="水平线 <hr> Ctrl-R";var m="撤销 Ctrl-Z";var j="重做 Ctrl-Y";var B="<p style='margin-top: 0px'><b>输入图片地址</b></p><p>示例:<br />http://www.cnprog.com/images/temp.jpg \"我的截图\"</p>";var D="<p style='margin-top: 0px'><b>输入Web地址</b></p><p>示例:<br />http://www.cnprog.com/ \"我的网站\"</p>";var n='<div>或者上传本地图片:</div><input type="file" name="file-upload" id="file-upload" size="26" onchange="return ajaxFileUpload($(\'#image-url\'));"/><br><img id="loading" src="/media/images/indicator.gif" style="display:none;"/>';var b="http://";var g="http://";var o="images/";var A=500;var x=100;var k="http://wmd-editor.com/";var r="WMD website";var w="_blank";y.PanelCollection=function(){this.buttonBar=E.getElementById("wmd-button-bar");this.preview=E.getElementById("previewer");this.output=E.getElementById("wmd-output");this.input=E.getElementById("editor")};y.panels=undefined;y.ieCachedRange=null;y.ieRetardedClick=false;a.isVisible=function(F){if(window.getComputedStyle){return window.getComputedStyle(F,null).getPropertyValue("display")!=="none"}else{if(F.currentStyle){return F.currentStyle.display!=="none"}}};a.addEvent=function(G,F,H){if(G.attachEvent){G.attachEvent("on"+F,H)}else{G.addEventListener(F,H,false)}};a.removeEvent=function(G,F,H){if(G.detachEvent){G.detachEvent("on"+F,H)}else{G.removeEventListener(F,H,false)}};a.fixEolChars=function(F){F=F.replace(/\r\n/g,"\n");F=F.replace(/\r/g,"\n");return F};a.extendRegExp=function(H,J,G){if(J===null||J===undefined){J=""}if(G===null||G===undefined){G=""}var I=H.toString();var F;I=I.replace(/\/([gim]*)$/,"");F=s.$1;I=I.replace(/(^\/|\/$)/g,"");I=J+I+G;return new s(I,F)};a.createImage=function(F){var H=o+F;var G=E.createElement("img");G.className="wmd-button";G.src=H;return G};a.prompt=function(M,P,H){var I;var F;var K;var J=0;if(arguments.length==4){J=arguments[3]}if(P===undefined){P=""}var L=function(Q){var R=(Q.charCode||Q.keyCode);if(R===27){N(true)}};var N=function(Q){a.removeEvent(E.body,"keydown",L);var R=K.value;if(Q){R=null}else{R=R.replace("http://http://","http://");R=R.replace("http://https://","https://");R=R.replace("http://ftp://","ftp://");if(R.indexOf("http://")===-1&&R.indexOf("ftp://")===-1){R="http://"+R}}I.parentNode.removeChild(I);F.parentNode.removeChild(F);H(R);return false};var G=function(){F=E.createElement("div");F.className="wmd-prompt-background";style=F.style;style.position="absolute";style.top="0";style.zIndex="1000";if(v.isKonqueror){style.backgroundColor="transparent"}else{if(v.isIE){style.filter="alpha(opacity=50)"}else{style.opacity="0.5"}}var Q=C.getPageSize();style.height=Q[1]+"px";if(v.isIE){style.left=E.documentElement.scrollLeft;style.width=E.documentElement.clientWidth}else{style.left="0";style.width="100%"}E.body.appendChild(F)};var O=function(){I=E.createElement("div");I.className="wmd-prompt-dialog";I.style.padding="10px;";I.style.position="fixed";I.style.width="400px";I.style.zIndex="1001";var Q=E.createElement("div");Q.innerHTML=M;Q.style.padding="5px";I.appendChild(Q);var S=E.createElement("form");S.onsubmit=function(){return N(false)};style=S.style;style.padding="0";style.margin="0";style.cssFloat="left";style.width="100%";style.textAlign="center";style.position="relative";I.appendChild(S);K=E.createElement("input");if(J==1){K.id="image-url"}K.type="text";K.value=P;style=K.style;style.display="block";style.width="80%";style.marginLeft=style.marginRight="auto";S.appendChild(K);if(J==1){var R=E.createElement("div");R.innerHTML=n;R.style.padding="5px";S.appendChild(R)}var U=E.createElement("input");U.type="button";U.onclick=function(){return N(false)};U.value="OK";style=U.style;style.margin="10px";style.display="inline";style.width="7em";var T=E.createElement("input");T.type="button";T.onclick=function(){return N(true)};T.value="Cancel";style=T.style;style.margin="10px";style.display="inline";style.width="7em";if(/mac/.test(l.platform.toLowerCase())){S.appendChild(T);S.appendChild(U)}else{S.appendChild(U);S.appendChild(T)}a.addEvent(E.body,"keydown",L);I.style.top="50%";I.style.left="50%";I.style.display="block";if(v.isIE_5or6){I.style.position="absolute";I.style.top=E.documentElement.scrollTop+200+"px";I.style.left="50%"}E.body.appendChild(I);I.style.marginTop=-(C.getHeight(I)/2)+"px";I.style.marginLeft=-(C.getWidth(I)/2)+"px"};G();top.setTimeout(function(){O();var R=P.length;if(K.selectionStart!==undefined){K.selectionStart=0;K.selectionEnd=R}else{if(K.createTextRange){var Q=K.createTextRange();Q.collapse(false);Q.moveStart("character",-R);Q.moveEnd("character",R);Q.select()}}K.focus()},0)};C.getTop=function(H,G){var F=H.offsetTop;if(!G){while(H=H.offsetParent){F+=H.offsetTop}}return F};C.getHeight=function(F){return F.offsetHeight||F.scrollHeight};C.getWidth=function(F){return F.offsetWidth||F.scrollWidth};C.getPageSize=function(){var G,H;var F,K;if(self.innerHeight&&self.scrollMaxY){G=E.body.scrollWidth;H=self.innerHeight+self.scrollMaxY}else{if(E.body.scrollHeight>E.body.offsetHeight){G=E.body.scrollWidth;H=E.body.scrollHeight}else{G=E.body.offsetWidth;H=E.body.offsetHeight}}if(self.innerHeight){F=self.innerWidth;K=self.innerHeight}else{if(E.documentElement&&E.documentElement.clientHeight){F=E.documentElement.clientWidth;K=E.documentElement.clientHeight}else{if(E.body){F=E.body.clientWidth;K=E.body.clientHeight}}}var J=Math.max(G,F);var I=Math.max(H,K);return[J,I,F,K]};y.inputPoller=function(O,H){var F=this;var K=y.panels.input;var G;var I;var L;var J;this.tick=function(){if(!a.isVisible(K)){return}if(K.selectionStart||K.selectionStart===0){var Q=K.selectionStart;var P=K.selectionEnd;if(Q!=G||P!=I){G=Q;I=P;if(L!=K.value){L=K.value;return true}}}return false};var N=function(){if(!a.isVisible(K)){return}if(F.tick()){O()}};var M=function(){J=top.setInterval(N,H)};this.destroy=function(){top.clearInterval(J)};M()};y.undoManager=function(Q){var U=this;var O=[];var M=0;var L="none";var G;var R;var H;var K;var F=function(W,V){if(L!=W){L=W;if(!V){I()}}if(!v.isIE||L!="moving"){H=top.setTimeout(N,1)}else{K=null}};var N=function(){K=new y.TextareaState();R.tick();H=undefined};this.setCommandMode=function(){L="command";I();H=top.setTimeout(N,0)};this.canUndo=function(){return M>1};this.canRedo=function(){if(O[M+1]){return true}return false};this.undo=function(){if(U.canUndo()){if(G){G.restore();G=null}else{O[M]=new y.TextareaState();O[--M].restore();if(Q){Q()}}}L="none";y.panels.input.focus();N()};this.redo=function(){if(U.canRedo()){O[++M].restore();if(Q){Q()}}L="none";y.panels.input.focus();N()};var I=function(){var V=K||new y.TextareaState();if(!V){return false}if(L=="moving"){if(!G){G=V}return}if(G){if(O[M-1].text!=G.text){O[M++]=G}G=null}O[M++]=V;O[M+1]=null;if(Q){Q()}};var P=function(V){var X=false;if(V.ctrlKey||V.metaKey){var W=V.charCode||V.keyCode;var Y=String.fromCharCode(W);switch(Y){case"y":U.redo();X=true;break;case"z":if(!V.shiftKey){U.undo()}else{U.redo()}X=true;break}}if(X){if(V.preventDefault){V.preventDefault()}if(top.event){top.event.returnValue=false}return}};var T=function(V){if(!V.ctrlKey&&!V.metaKey){var W=V.keyCode;if((W>=33&&W<=40)||(W>=63232&&W<=63235)){F("moving")}else{if(W==8||W==46||W==127){F("deleting")}else{if(W==13){F("newlines")}else{if(W==27){F("escape")}else{if((W<16||W>20)&&W!=91){F("typing")}}}}}}};var J=function(){a.addEvent(y.panels.input,"keypress",function(W){if((W.ctrlKey||W.metaKey)&&(W.keyCode==89||W.keyCode==90)){W.preventDefault()}});var V=function(){if(v.isIE||(K&&K.text!=y.panels.input.value)){if(H==undefined){L="paste";I();N()}}};R=new y.inputPoller(V,x);a.addEvent(y.panels.input,"keydown",P);a.addEvent(y.panels.input,"keydown",T);a.addEvent(y.panels.input,"mousedown",function(){F("moving")});y.panels.input.onpaste=V;y.panels.input.ondrop=V};var S=function(){J();N();I()};this.destroy=function(){if(R){R.destroy()}};S()};y.editor=function(O){if(!O){O=function(){}}var L=y.panels.input;var I=0;var P=this;var K;var R;var G;var M;var N;var U=function(W){L.focus();if(W.textOp){if(N){N.setCommandMode()}var Y=new y.TextareaState();if(!Y){return}var Z=Y.getChunks();var V=function(){L.focus();if(Z){Y.setChunks(Z)}Y.restore();O()};var X=W.textOp(Z,V);if(!X){V()}}if(W.execute){W.execute(P)}};var S=function(){if(N){F(document.getElementById("wmd-undo-button"),N.canUndo());F(document.getElementById("wmd-redo-button"),N.canRedo())}};var F=function(V,X){var Y="0px";var Z="-20px";var W="-40px";if(X){V.style.backgroundPosition=V.XShift+" "+Y;V.onmouseover=function(){this.style.backgroundPosition=this.XShift+" "+W};V.onmouseout=function(){this.style.backgroundPosition=this.XShift+" "+Y};if(v.isIE){V.onmousedown=function(){y.ieRetardedClick=true;y.ieCachedRange=document.selection.createRange()}}if(!V.isHelp){V.onclick=function(){if(this.onmouseout){this.onmouseout()}U(this);return false}}}else{V.style.backgroundPosition=V.XShift+" "+Z;V.onmouseover=V.onmouseout=V.onclick=function(){}}};var J=function(){var Z=document.getElementById("wmd-button-bar");var W="0px";var Y="-20px";var ae="-40px";var ak=document.createElement("ul");ak.id="wmd-button-row";ak=Z.appendChild(ak);var ad=document.createElement("li");ad.className="wmd-button";ad.id="wmd-bold-button";ad.title=c;ad.XShift="0px";ad.textOp=h.doBold;F(ad,true);ak.appendChild(ad);var ac=document.createElement("li");ac.className="wmd-button";ac.id="wmd-italic-button";ac.title=f;ac.XShift="-20px";ac.textOp=h.doItalic;F(ac,true);ak.appendChild(ac);var ah=document.createElement("li");ah.className="wmd-spacer";ah.id="wmd-spacer1";ak.appendChild(ah);var ai=document.createElement("li");ai.className="wmd-button";ai.id="wmd-link-button";ai.title=z;ai.XShift="-40px";ai.textOp=function(ap,aq){return h.doLinkOrImage(ap,aq,false)};F(ai,true);ak.appendChild(ai);var al=document.createElement("li");al.className="wmd-button";al.id="wmd-quote-button";al.title=u;al.XShift="-60px";al.textOp=h.doBlockquote;F(al,true);ak.appendChild(al);var am=document.createElement("li");am.className="wmd-button";am.id="wmd-code-button";am.title=e;am.XShift="-80px";am.textOp=h.doCode;F(am,true);ak.appendChild(am);var aa=document.createElement("li");aa.className="wmd-button";aa.id="wmd-image-button";aa.title=d;aa.XShift="-100px";aa.textOp=function(ap,aq){return h.doLinkOrImage(ap,aq,true)};F(aa,true);ak.appendChild(aa);var ag=document.createElement("li");ag.className="wmd-spacer";ag.id="wmd-spacer2";ak.appendChild(ag);var ab=document.createElement("li");ab.className="wmd-button";ab.id="wmd-olist-button";ab.title=q;ab.XShift="-120px";ab.textOp=function(ap,aq){h.doList(ap,aq,true)};F(ab,true);ak.appendChild(ab);var ao=document.createElement("li");ao.className="wmd-button";ao.id="wmd-ulist-button";ao.title=t;ao.XShift="-140px";ao.textOp=function(ap,aq){h.doList(ap,aq,false)};F(ao,true);ak.appendChild(ao);var aj=document.createElement("li");aj.className="wmd-button";aj.id="wmd-heading-button";aj.title=i;aj.XShift="-160px";aj.textOp=h.doHeading;F(aj,true);ak.appendChild(aj);var X=document.createElement("li");X.className="wmd-button";X.id="wmd-hr-button";X.title=p;X.XShift="-180px";X.textOp=h.doHorizontalRule;F(X,true);ak.appendChild(X);var af=document.createElement("li");af.className="wmd-spacer";af.id="wmd-spacer3";ak.appendChild(af);var V=document.createElement("li");V.className="wmd-button";V.id="wmd-undo-button";V.title=m;V.XShift="-200px";V.execute=function(ap){ap.undo()};F(V,true);ak.appendChild(V);var an=document.createElement("li");an.className="wmd-button";an.id="wmd-redo-button";an.title=j;if(/win/.test(l.platform.toLowerCase())){an.title=j}else{an.title="重做 - Ctrl+Shift+Z"}an.XShift="-220px";an.execute=function(ap){ap.redo()};F(an,true);ak.appendChild(an);S()};var H=function(){if(/\?noundo/.test(E.location.href)){y.nativeUndo=true}if(!y.nativeUndo){N=new y.undoManager(function(){O();S()})}J();var W="keydown";if(v.isOpera){W="keypress"}a.addEvent(L,W,function(Y){if(Y.ctrlKey||Y.metaKey){var Z=Y.charCode||Y.keyCode;var X=String.fromCharCode(Z).toLowerCase();if(Z===46){X=""}if(Z===190){X="."}switch(X){case"b":U(document.getElementById("wmd-bold-button"));break;case"i":U(document.getElementById("wmd-italic-button"));break;case"l":U(document.getElementById("wmd-link-button"));break;case".":U(document.getElementById("wmd-quote-button"));break;case"k":U(document.getElementById("wmd-code-button"));break;case"g":U(document.getElementById("wmd-image-button"));break;case"o":U(document.getElementById("wmd-olist-button"));break;case"u":U(document.getElementById("wmd-ulist-button"));break;case"h":U(document.getElementById("wmd-heading-button"));break;case"r":U(document.getElementById("wmd-hr-button"));break;case"y":U(document.getElementById("wmd-redo-button"));break;case"z":if(Y.shiftKey){U(document.getElementById("wmd-redo-button"))}else{U(document.getElementById("wmd-undo-button"))}break;default:return}if(Y.preventDefault){Y.preventDefault()}if(top.event){top.event.returnValue=false}}});a.addEvent(L,"keyup",function(X){if(X.shiftKey&&!X.ctrlKey&&!X.metaKey){var Y=X.charCode||X.keyCode;if(Y===13){fakeButton={};fakeButton.textOp=h.doAutoindent;U(fakeButton)}}});if(L.form){var V=L.form.onsubmit;L.form.onsubmit=function(){Q();if(V){return V.apply(this,arguments)}}}};var Q=function(){if(y.showdown){var V=new y.showdown.converter()}var W=L.value;var X=function(){L.value=W};if(!/markdown/.test(y.wmd_env.output.toLowerCase())){if(V){L.value=V.makeHtml(W);top.setTimeout(X,0)}}return true};this.undo=function(){if(N){N.undo()}};this.redo=function(){if(N){N.redo()}};var T=function(){H()};this.destroy=function(){if(N){N.destroy()}if(G.parentNode){G.parentNode.removeChild(G)}if(L){L.style.marginTop=""}top.clearInterval(M)};T()};y.TextareaState=function(){var F=this;var G=y.panels.input;this.init=function(){if(!a.isVisible(G)){return}this.setInputAreaSelectionStartEnd();this.scrollTop=G.scrollTop;if(!this.text&&G.selectionStart||G.selectionStart===0){this.text=G.value}};this.setInputAreaSelection=function(){if(!a.isVisible(G)){return}if(G.selectionStart!==undefined&&!v.isOpera){G.focus();G.selectionStart=F.start;G.selectionEnd=F.end;G.scrollTop=F.scrollTop}else{if(E.selection){if(E.activeElement&&E.activeElement!==G){return}G.focus();var H=G.createTextRange();H.moveStart("character",-G.value.length);H.moveEnd("character",-G.value.length);H.moveEnd("character",F.end);H.moveStart("character",F.start);H.select()}}};this.setInputAreaSelectionStartEnd=function(){if(G.selectionStart||G.selectionStart===0){F.start=G.selectionStart;F.end=G.selectionEnd}else{if(E.selection){F.text=a.fixEolChars(G.value);var K;if(y.ieRetardedClick&&y.ieCachedRange){K=y.ieCachedRange;y.ieRetardedClick=false}else{K=E.selection.createRange()}var L=a.fixEolChars(K.text);var J="\x07";var I=J+L+J;K.text=I;var M=a.fixEolChars(G.value);K.moveStart("character",-I.length);K.text=L;F.start=M.indexOf(J);F.end=M.lastIndexOf(J)-J.length;var H=F.text.length-a.fixEolChars(G.value).length;if(H){K.moveStart("character",-L.length);while(H--){L+="\n";F.end+=1}K.text=L}this.setInputAreaSelection()}}};this.restore=function(){if(F.text!=undefined&&F.text!=G.value){G.value=F.text}this.setInputAreaSelection();G.scrollTop=F.scrollTop};this.getChunks=function(){var H=new y.Chunks();H.before=a.fixEolChars(F.text.substring(0,F.start));H.startTag="";H.selection=a.fixEolChars(F.text.substring(F.start,F.end));H.endTag="";H.after=a.fixEolChars(F.text.substring(F.end));H.scrollTop=F.scrollTop;return H};this.setChunks=function(H){H.before=H.before+H.startTag;H.after=H.endTag+H.after;if(v.isOpera){H.before=H.before.replace(/\n/g,"\r\n");H.selection=H.selection.replace(/\n/g,"\r\n");H.after=H.after.replace(/\n/g,"\r\n")}this.start=H.before.length;this.end=H.before.length+H.selection.length;this.text=H.before+H.selection+H.after;this.scrollTop=H.scrollTop};this.init()};y.Chunks=function(){};y.Chunks.prototype.findTags=function(G,I){var F=this;var H;if(G){H=a.extendRegExp(G,"","$");this.before=this.before.replace(H,function(J){F.startTag=F.startTag+J;return""});H=a.extendRegExp(G,"^","");this.selection=this.selection.replace(H,function(J){F.startTag=F.startTag+J;return""})}if(I){H=a.extendRegExp(I,"","$");this.selection=this.selection.replace(H,function(J){F.endTag=J+F.endTag;return""});H=a.extendRegExp(I,"^","");this.after=this.after.replace(H,function(J){F.endTag=J+F.endTag;return""})}};y.Chunks.prototype.trimWhitespace=function(F){this.selection=this.selection.replace(/^(\s*)/,"");if(!F){this.before+=s.$1}this.selection=this.selection.replace(/(\s*)$/,"");if(!F){this.after=s.$1+this.after}};y.Chunks.prototype.skipLines=function(H,G,F){if(H===undefined){H=1}if(G===undefined){G=1}H++;G++;var I;var J;this.selection=this.selection.replace(/(^\n*)/,"");this.startTag=this.startTag+s.$1;this.selection=this.selection.replace(/(\n*$)/,"");this.endTag=this.endTag+s.$1;this.startTag=this.startTag.replace(/(^\n*)/,"");this.before=this.before+s.$1;this.endTag=this.endTag.replace(/(\n*$)/,"");this.after=this.after+s.$1;if(this.before){I=J="";while(H--){I+="\\n?";J+="\n"}if(F){I="\\n*"}this.before=this.before.replace(new s(I+"$",""),J)}if(this.after){I=J="";while(G--){I+="\\n?";J+="\n"}if(F){I="\\n*"}this.after=this.after.replace(new s(I,""),J)}};h.prefixes="(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)";h.unwrap=function(G){var F=new s("([^\\n])\\n(?!(\\n|"+h.prefixes+"))","g");G.selection=G.selection.replace(F,"$1 $2")};h.wrap=function(G,F){h.unwrap(G);var H=new s("(.{1,"+F+"})( +|$\\n?)","gm");G.selection=G.selection.replace(H,function(I,J){if(new s("^"+h.prefixes,"").test(I)){return I}return J+"\n"});G.selection=G.selection.replace(/\s+$/,"")};h.doBold=function(F,G){return h.doBorI(F,G,2,"strong text")};h.doItalic=function(F,G){return h.doBorI(F,G,1,"emphasized text")};h.doBorI=function(L,J,K,F){L.trimWhitespace();L.selection=L.selection.replace(/\n{2,}/g,"\n");L.before.search(/(\**$)/);var I=s.$1;L.after.search(/(^\**)/);var G=s.$1;var M=Math.min(I.length,G.length);if((M>=K)&&(M!=2||K!=1)){L.before=L.before.replace(s("[*]{"+K+"}$",""),"");L.after=L.after.replace(s("^[*]{"+K+"}",""),"")}else{if(!L.selection&&G){L.after=L.after.replace(/^([*_]*)/,"");L.before=L.before.replace(/(\s?)$/,"");var H=s.$1;L.before=L.before+G+H}else{if(!L.selection&&!G){L.selection=F}var N=K<=1?"*":"**";L.before=L.before+N;L.after=N+L.after}}return};h.stripLinkDefs=function(G,F){G=G.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,function(K,L,H,I,J){F[L]=K.replace(/\s*$/,"");if(I){F[L]=K.replace(/["(](.+?)[")]$/,"");return I+J}return""});return G};h.addLinkDef=function(M,I){var F=0;var H={};M.before=h.stripLinkDefs(M.before,H);M.selection=h.stripLinkDefs(M.selection,H);M.after=h.stripLinkDefs(M.after,H);var G="";var L=/(\[(?:\[[^\]]*\]|[^\[\]])*\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;var K=function(O){F++;O=O.replace(/^[ ]{0,3}\[(\d+)\]:/," ["+F+"]:");G+="\n"+O};var J=function(P,Q,R,O){if(H[R]){K(H[R]);return Q+F+O}return P};M.before=M.before.replace(L,J);if(I){K(I)}else{M.selection=M.selection.replace(L,J)}var N=F;M.after=M.after.replace(L,J);if(M.after){M.after=M.after.replace(/\n*$/,"")}if(!M.after){M.selection=M.selection.replace(/\n*$/,"")}M.after+="\n\n"+G;return N};h.doLinkOrImage=function(F,G,I){F.trimWhitespace();F.findTags(/\s*!?\[/,/\][ ]?(?:\n[ ]*)?(\[.*?\])?/);if(F.endTag.length>1){F.startTag=F.startTag.replace(/!?\[/,"");F.endTag="";h.addLinkDef(F,null)}else{if(/\n\n/.test(F.selection)){h.addLinkDef(F,null);return}var H=function(L){if(L!==null){F.startTag=F.endTag="";var K=" [999]: "+L;var J=h.addLinkDef(F,K);F.startTag=I?"![":"[";F.endTag="]["+J+"]";if(!F.selection){if(I){F.selection="alt text"}else{F.selection="link text"}}}G()};if(I){a.prompt(B,b,H,1)}else{a.prompt(D,g,H)}return true}};a.makeAPI=function(){y.wmd={};y.wmd.editor=y.editor;y.wmd.previewManager=y.previewManager};a.startEditor=function(){if(y.wmd_env.autostart===false){a.makeAPI();return}var G;var F;var H=function(){y.panels=new y.PanelCollection();F=new y.previewManager();var I=F.refresh;G=new y.editor(I);F.refresh(true)};a.addEvent(top,"load",H)};y.previewManager=function(){var H=this;var V;var F;var N;var M;var S;var O;var I=3000;var P="delayed";var K=function(X,Y){a.addEvent(X,"input",Y);X.onpaste=Y;X.ondrop=Y;a.addEvent(X,"keypress",Y);a.addEvent(X,"keydown",Y);F=new y.inputPoller(Y,A)};var R=function(){var X=0;if(top.innerHeight){X=top.pageYOffset}else{if(E.documentElement&&E.documentElement.scrollTop){X=E.documentElement.scrollTop}else{if(E.body){X=E.body.scrollTop}}}return X};var L=function(){if(!y.panels.preview&&!y.panels.output){return}var Z=y.panels.input.value;if(Z&&Z==S){return}else{S=Z}var Y=new Date().getTime();if(!V&&y.showdown){V=new y.showdown.converter()}if(V){Z=V.makeHtml(Z)}var X=new Date().getTime();M=X-Y;G(Z);O=Z};var U=function(){if(N){top.clearTimeout(N);N=undefined}if(P!=="manual"){var X=0;if(P==="delayed"){X=M}if(X>I){X=I}N=top.setTimeout(L,X)}};var J=function(X){if(X.scrollHeight<=X.clientHeight){return 1}return X.scrollTop/(X.scrollHeight-X.clientHeight)};var W=function(){if(y.panels.preview){y.panels.preview.scrollTop=(y.panels.preview.scrollHeight-y.panels.preview.clientHeight)*J(y.panels.preview)}if(y.panels.output){y.panels.output.scrollTop=(y.panels.output.scrollHeight-y.panels.output.clientHeight)*J(y.panels.output)}};this.refresh=function(X){if(X){S="";L()}else{U()}};this.processingTime=function(){return M};this.output=function(){return O};this.setUpdateMode=function(X){P=X;H.refresh()};var Q=true;var G=function(aa){var X=C.getTop(y.panels.input)-R();if(y.panels.output){if(y.panels.output.value!==undefined){y.panels.output.value=aa;y.panels.output.readOnly=true}else{var Z=aa.replace(/&/g,"&amp;");Z=Z.replace(/</g,"&lt;");y.panels.output.innerHTML="<pre><code>"+Z+"</code></pre>"}}if(y.panels.preview){y.panels.preview.innerHTML=aa}W();if(Q){Q=false;return}var Y=C.getTop(y.panels.input)-R();if(v.isIE){top.setTimeout(function(){top.scrollBy(0,Y-X)},0)}else{top.scrollBy(0,Y-X)}};var T=function(){K(y.panels.input,U);L();if(y.panels.preview){y.panels.preview.scrollTop=0}if(y.panels.output){y.panels.output.scrollTop=0}};this.destroy=function(){if(F){F.destroy()}};T()};h.doAutoindent=function(F,G){F.before=F.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/,"\n\n");F.before=F.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/,"\n\n");F.before=F.before.replace(/(\n|^)[ \t]+\n$/,"\n\n");if(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(F.before)){if(h.doList){h.doList(F)}}if(/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(F.before)){if(h.doBlockquote){h.doBlockquote(F)}}if(/(\n|^)(\t|[ ]{4,}).*\n$/.test(F.before)){if(h.doCode){h.doCode(F)}}};h.doBlockquote=function(F,G){F.selection=F.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,function(L,K,J,I){F.before+=K;F.after=I+F.after;return J});F.before=F.before.replace(/(>[ \t]*)$/,function(J,I){F.selection=I+F.selection;return""});F.selection=F.selection.replace(/^(\s|>)+$/,"");F.selection=F.selection||"Blockquote";if(F.before){F.before=F.before.replace(/\n?$/,"\n")}if(F.after){F.after=F.after.replace(/^\n?/,"\n")}F.before=F.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/,function(I){F.startTag=I;return""});F.after=F.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,function(I){F.endTag=I;return""});var H=function(J){var I=J?"> ":"";if(F.startTag){F.startTag=F.startTag.replace(/\n((>|\s)*)\n$/,function(L,K){return"\n"+K.replace(/^[ ]{0,3}>?[ \t]*$/gm,I)+"\n"})}if(F.endTag){F.endTag=F.endTag.replace(/^\n((>|\s)*)\n/,function(L,K){return"\n"+K.replace(/^[ ]{0,3}>?[ \t]*$/gm,I)+"\n"})}};if(/^(?![ ]{0,3}>)/m.test(F.selection)){h.wrap(F,y.wmd_env.lineLength-2);F.selection=F.selection.replace(/^/gm,"> ");H(true);F.skipLines()}else{F.selection=F.selection.replace(/^[ ]{0,3}> ?/gm,"");h.unwrap(F);H(false);if(!/^(\n|^)[ ]{0,3}>/.test(F.selection)&&F.startTag){F.startTag=F.startTag.replace(/\n{0,2}$/,"\n\n")}if(!/(\n|^)[ ]{0,3}>.*$/.test(F.selection)&&F.endTag){F.endTag=F.endTag.replace(/^\n{0,2}/,"\n\n")}}if(!/\n/.test(F.selection)){F.selection=F.selection.replace(/^(> *)/,function(I,J){F.startTag+=J;return""})}};h.doCode=function(F,G){var I=/\S[ ]*$/.test(F.before);var K=/^[ ]*\S/.test(F.after);if((!K&&!I)||/\n/.test(F.selection)){F.before=F.before.replace(/[ ]{4}$/,function(L){F.selection=L+F.selection;return""});var J=1;var H=1;if(/\n(\t|[ ]{4,}).*\n$/.test(F.before)){J=0}if(/^\n(\t|[ ]{4,})/.test(F.after)){H=0}F.skipLines(J,H);if(!F.selection){F.startTag=" ";F.selection="enter code here"}else{if(/^[ ]{0,3}\S/m.test(F.selection)){F.selection=F.selection.replace(/^/gm," ")}else{F.selection=F.selection.replace(/^[ ]{4}/gm,"")}}}else{F.trimWhitespace();F.findTags(/`/,/`/);if(!F.startTag&&!F.endTag){F.startTag=F.endTag="`";if(!F.selection){F.selection="enter code here"}}else{if(F.endTag&&!F.startTag){F.before+=F.endTag;F.endTag=""}else{F.startTag=F.endTag=""}}}};h.doList=function(Q,J,I){var S=/(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;var R=/^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/;var F="-";var N=1;var L=function(){var T;if(I){T=" "+N+". ";N++}else{T=" "+F+" "}return T};var M=function(T){if(I===undefined){I=/^\s*\d/.test(T)}T=T.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,function(U){return L()});return T};Q.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/,null);if(Q.before&&!/\n$/.test(Q.before)&&!/^\n/.test(Q.startTag)){Q.before+=Q.startTag;Q.startTag=""}if(Q.startTag){var H=/\d+[.]/.test(Q.startTag);Q.startTag="";Q.selection=Q.selection.replace(/\n[ ]{4}/g,"\n");h.unwrap(Q);Q.skipLines();if(H){Q.after=Q.after.replace(R,M)}if(I==H){return}}var K=1;Q.before=Q.before.replace(S,function(T){if(/^\s*([*+-])/.test(T)){F=s.$1}K=/[^\n]\n\n[^\n]/.test(T)?1:0;return M(T)});if(!Q.selection){Q.selection="List item"}var O=L();var G=1;Q.after=Q.after.replace(R,function(T){G=/[^\n]\n\n[^\n]/.test(T)?1:0;return M(T)});Q.trimWhitespace(true);Q.skipLines(K,G,true);Q.startTag=O;var P=O.replace(/./g," ");h.wrap(Q,y.wmd_env.lineLength-P.length);Q.selection=Q.selection.replace(/\n/g,"\n"+P)};h.doHeading=function(H,I){H.selection=H.selection.replace(/\s+/g," ");H.selection=H.selection.replace(/(^\s+|\s+$)/g,"");if(!H.selection){H.startTag="## ";H.selection="Heading";H.endTag=" ##";return}var J=0;H.findTags(/#+[ ]*/,/[ ]*#+/);if(/#+/.test(H.startTag)){J=s.lastMatch.length}H.startTag=H.endTag="";H.findTags(null,/\s?(-+|=+)/);if(/=+/.test(H.endTag)){J=1}if(/-+/.test(H.endTag)){J=2}H.startTag=H.endTag="";H.skipLines(1,1);var K=J==0?2:J-1;if(K>0){var G=K>=2?"-":"=";var F=H.selection.length;if(F>y.wmd_env.lineLength){F=y.wmd_env.lineLength}H.endTag="\n";while(F--){H.endTag+=G}}};h.doHorizontalRule=function(F,G){F.startTag="----------\n";F.selection="";F.skipLines(2,1,true)}};Attacklab.wmd_env={};Attacklab.account_options={};Attacklab.wmd_defaults={version:1,output:"Markdown",lineLength:40,delayLoad:false};if(!Attacklab.wmd){Attacklab.wmd=function(){Attacklab.loadEnv=function(){var b=function(d){if(!d){return}for(var c in d){Attacklab.wmd_env[c]=d[c]}};b(Attacklab.wmd_defaults);b(Attacklab.account_options);b(top.wmd_options);Attacklab.full=true;var a="bold italic link blockquote code image ol ul heading hr";Attacklab.wmd_env.buttons=Attacklab.wmd_env.buttons||a};Attacklab.loadEnv()};Attacklab.wmd();Attacklab.wmdBase();Attacklab.Util.startEditor()}; \ No newline at end of file
diff --git a/templates/content/js/wmd/wmd-test.html b/forum/skins/default/media/js/wmd/wmd-test.html
index d748501a..d748501a 100644
--- a/templates/content/js/wmd/wmd-test.html
+++ b/forum/skins/default/media/js/wmd/wmd-test.html
diff --git a/templates/content/js/wmd/wmd.css b/forum/skins/default/media/js/wmd/wmd.css
index 80c226c8..80c226c8 100644
--- a/templates/content/js/wmd/wmd.css
+++ b/forum/skins/default/media/js/wmd/wmd.css
diff --git a/templates/content/js/wmd/wmd.js b/forum/skins/default/media/js/wmd/wmd.js
index e396d3cb..70271d4d 100644
--- a/templates/content/js/wmd/wmd.js
+++ b/forum/skins/default/media/js/wmd/wmd.js
@@ -21,11 +21,11 @@ Attacklab.wmdBase = function(){
// Used to work around some browser bugs where we can't use feature testing.
- global.isIE = /msie/.test(nav.userAgent.toLowerCase());
- global.isIE_5or6 = /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase());
- global.isIE_7plus = global.isIE && !global.isIE_5or6;
- global.isOpera = /opera/.test(nav.userAgent.toLowerCase());
- global.isKonqueror = /konqueror/.test(nav.userAgent.toLowerCase());
+ global.isIE = /msie/.test(nav.userAgent.toLowerCase());
+ global.isIE_5or6 = /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase());
+ global.isIE_7plus = global.isIE && !global.isIE_5or6;
+ global.isOpera = /opera/.test(nav.userAgent.toLowerCase());
+ global.isKonqueror = /konqueror/.test(nav.userAgent.toLowerCase());
var toolbar_strong_label = $.i18n._('bold') + " <strong> Ctrl-B";
var toolbar_emphasis_label = $.i18n._('italic') + " <em> Ctrl-I";
@@ -53,8 +53,8 @@ Attacklab.wmdBase = function(){
var linkDialogText = "<p style='margin-top: 0px'>" + $.i18n._('enter url') + '</p>';
var uploadImageHTML ="<div>" + $.i18n._('upload image') + "</div>" +
"<input type=\"file\" name=\"file-upload\" id=\"file-upload\" size=\"26\" "+
- "onchange=\"return ajaxFileUpload($('#image-url'));\"/><br>" +
- "<img id=\"loading\" src=\"" + scriptUrl + "content/images/indicator.gif\" style=\"display:none;\"/>";
+ "onchange=\"return ajaxFileUpload($('#image-url'));\"/><br>" +
+ "<img id=\"loading\" src=\"" + mediaUrl("media/images/indicator.gif") + "\" style=\"display:none;\"/>";
// The default text that appears in the dialog input box when entering
// links.
@@ -115,7 +115,7 @@ Attacklab.wmdBase = function(){
}
else if (elem.currentStyle) {
// IE
- return elem.currentStyle["display"] !== "none";
+ return elem.currentStyle.display !== "none";
}
};
@@ -185,7 +185,7 @@ Attacklab.wmdBase = function(){
pattern = pre + pattern + post;
return new re(pattern, flags);
- }
+ };
// Sets the image for a button passed to the WMD editor.
@@ -218,8 +218,9 @@ Attacklab.wmdBase = function(){
var input; // The text box where you enter the hyperlink.
var type = 0;
// The dialog box type(0: Link, 1: Image)
- if(arguments.length == 4)
+ if(arguments.length == 4){
type = arguments[3];
+ }
if (defaultInputText === undefined) {
defaultInputText = "";
@@ -334,8 +335,9 @@ Attacklab.wmdBase = function(){
// The input text box
input = doc.createElement("input");
- if(type == 1)
+ if(type == 1){
input.id = "image-url";
+ }
input.type = "text";
input.value = defaultInputText;
style = input.style;
@@ -347,9 +349,9 @@ Attacklab.wmdBase = function(){
// The upload file input
if(type == 1){
var upload = doc.createElement("div");
- upload.innerHTML = uploadImageHTML;
- upload.style.padding = "5px";
- form.appendChild(upload);
+ upload.innerHTML = uploadImageHTML;
+ upload.style.padding = "5px";
+ form.appendChild(upload);
}
// The ok button
@@ -433,9 +435,10 @@ Attacklab.wmdBase = function(){
position.getTop = function(elem, isInner){
var result = elem.offsetTop;
if (!isInner) {
- while (elem = elem.offsetParent) {
- result += elem.offsetTop;
- }
+ while (elem.offsetParent) {
+ elem = elem.offsetParent;
+ result += elem.offsetTop;
+ }
}
return result;
};
@@ -762,7 +765,7 @@ Attacklab.wmdBase = function(){
var handlePaste = function(){
if (global.isIE || (inputStateObj && inputStateObj.text != wmd.panels.input.value)) {
- if (timer == undefined) {
+ if (timer === undefined) {
mode = "paste";
saveState();
refreshState();
@@ -923,17 +926,16 @@ Attacklab.wmdBase = function(){
}
doClick(this);
return false;
- }
+ };
}
}
else {
button.style.backgroundPosition = button.XShift + " " + disabledYShift;
button.onmouseover = button.onmouseout = button.onclick = function(){};
}
- }
-
+ };
+
var makeSpritedButtonRow = function(){
-
var buttonBar = document.getElementById("wmd-button-bar");
var normalYShift = "0px";
var disabledYShift = "-20px";
@@ -1102,7 +1104,7 @@ Attacklab.wmdBase = function(){
buttonRow.appendChild(helpButton);
*/
setUndoRedoButtonStates();
- }
+ };
var setupEditor = function(){
@@ -1298,7 +1300,7 @@ Attacklab.wmdBase = function(){
this.text = inputArea.value;
}
- }
+ };
// Sets the selected text in the input box after we've performed an
// operation.
@@ -1384,7 +1386,7 @@ Attacklab.wmdBase = function(){
// Restore this state into the input area.
this.restore = function(){
- if (stateObj.text != undefined && stateObj.text != inputArea.value) {
+ if (stateObj.text !== undefined && stateObj.text != inputArea.value) {
inputArea.value = stateObj.text;
}
this.setInputAreaSelection();
@@ -1903,12 +1905,10 @@ Attacklab.wmdBase = function(){
if (wmd.panels.preview) {
wmd.panels.preview.scrollTop = (wmd.panels.preview.scrollHeight - wmd.panels.preview.clientHeight) * getScaleFactor(wmd.panels.preview);
- ;
}
if (wmd.panels.output) {
wmd.panels.output.scrollTop = (wmd.panels.output.scrollHeight - wmd.panels.output.clientHeight) * getScaleFactor(wmd.panels.output);
- ;
}
};
diff --git a/templates/content/js/yuicompressor-2.4.2.jar b/forum/skins/default/media/js/yuicompressor-2.4.2.jar
index c29470bd..c29470bd 100644
--- a/templates/content/js/yuicompressor-2.4.2.jar
+++ b/forum/skins/default/media/js/yuicompressor-2.4.2.jar
Binary files differ
diff --git a/templates/content/style/default.css b/forum/skins/default/media/style/default.css
index 2bc185ad..27da1dab 100644
--- a/templates/content/style/default.css
+++ b/forum/skins/default/media/style/default.css
@@ -7,9 +7,9 @@ Style sheet for cnprog.com
All rights reserved. 2008 CNPROG.COM
*/
-@import url(content/style/jquery.autocomplete.css);
-@import url(content/style/openid.css);
-@import url(content/style/prettify.css);
+@import url(media/style/jquery.autocomplete.css);
+@import url(media/style/openid.css);
+@import url(media/style/prettify.css);
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, form, label, table, caption, tbody, tfoot, thead, tr, th, td
{
diff --git a/templates/content/style/jquery.autocomplete.css b/forum/skins/default/media/style/jquery.autocomplete.css
index 3bf2c2d9..3bf2c2d9 100644
--- a/templates/content/style/jquery.autocomplete.css
+++ b/forum/skins/default/media/style/jquery.autocomplete.css
diff --git a/templates/content/style/openid.css b/forum/skins/default/media/style/openid.css
index 0d201df2..0d201df2 100644
--- a/templates/content/style/openid.css
+++ b/forum/skins/default/media/style/openid.css
diff --git a/templates/content/style/prettify.css b/forum/skins/default/media/style/prettify.css
index 10a37577..10a37577 100644
--- a/templates/content/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
new file mode 100644
index 00000000..02148ab0
--- /dev/null
+++ b/forum/skins/default/media/style/style.css
@@ -0,0 +1,2459 @@
+@import url(jquery.autocomplete.css);
+@import url(openid.css);
+@import url(prettify.css);
+
+/* 公用 */
+body {
+ background: #FFF;
+ font-size: 12px;
+ line-height: 150%;
+ margin: 0;
+ padding: 0;
+ color: #000;
+ font-family: sans-serif;
+}
+
+div {
+ margin: 0 auto;
+ padding: 0;
+}
+
+h1, h2, h3, h4, h5, h6, ul, li, dl, dt, dd, form, img, p {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+
+label {
+ vertical-align: middle;
+}
+
+hr {
+ border: none;
+ border-top: 1px dashed #ccccce;
+}
+
+input, select {
+ vertical-align: middle;
+ font-family: Trebuchet MS, "segoe ui", Helvetica, "Microsoft YaHei", 宋 体, Tahoma, Verdana, MingLiu, PMingLiu, Arial, sans-serif;
+}
+
+p {
+ margin-bottom: 13px;
+ font-size: 13px;
+ line-height: 140%;
+}
+
+a {
+ color: #333333;
+ text-decoration: none;
+}
+
+.badges a {
+ color: #763333;
+ text-decoration: underline;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+.block {
+ width: 960px;
+ height: auto;
+}
+
+.fleft {
+ float: left;
+}
+
+.fright {
+ float: right;
+}
+
+.tleft {
+ text-align: left;
+}
+
+.tcenter {
+ text-align: center;
+}
+
+.tright {
+ text-align: right;
+}
+
+.dis {
+ display: block;
+}
+
+.inline {
+ display: inline;
+}
+
+.none {
+ display: none;
+}
+
+.red {
+ color: #CC0000;
+}
+
+.b {
+ font-weight: bold;
+}
+
+.f10 {
+ font-size: 10px;
+}
+
+.f11 {
+ font-size: 11px;
+}
+
+.f12 {
+ font-size: 12px;
+}
+
+.f13 {
+ font-size: 13px;
+}
+
+.f14 {
+ font-size: 14px;
+}
+
+.white {
+ color: #FFFFFF;
+}
+
+.u {
+ text-decoration: underline;
+}
+
+.spacer1 {
+ height: 6px;
+ line-height: 6px;
+ clear: both;
+ visibility: hidden;
+}
+
+.spacer3 {
+ height: 30px;
+ line-height: 30px;
+ clear: both;
+ visibility: hidden;
+}
+
+h1 {
+ font-size: 160%;
+ padding: 5px 0 5px 0;
+}
+
+h2 {
+ font-size: 140%;
+ padding: 3px 0 3px 0;
+}
+
+h3 {
+ font-size: 120%;
+ padding: 3px 0 3px 0;
+}
+
+ul {
+ list-style: disc;
+ margin-left: 20px;
+ padding-left: 0px;
+ margin-bottom: 1em;
+}
+
+ol {
+ list-style: decimal;
+ margin-left: 30px;
+ margin-bottom: 1em;
+ padding-left: 0px;
+}
+
+td ul {
+ vertical-align: middle;
+}
+
+li input {
+ margin: 3px 3px 4px 3px;
+}
+
+pre {
+ font-family: Consolas, Monaco, Liberation Mono, Lucida Console, Monospace;
+ font-size: 100%;
+ margin-bottom: 10px;
+ overflow: auto;
+ width: 580px;
+ background-color: #F5F5F5;
+ padding-left: 5px;
+ padding-top: 5px;
+ padding-bottom: 20px ! ie7;
+}
+
+code {
+ font-family: Consolas, Monaco, Liberation Mono, Lucida Console, Monospace;
+ font-size: 100%;
+
+}
+
+blockquote {
+ margin-bottom: 10px;
+ margin-right: 15px;
+ padding: 10px 0px 1px 10px;
+ background-color: #F5F5F5;
+}
+
+/*页面布局*/
+#wrapper {
+ width: 960px;
+ margin: auto;
+ padding: 0;
+}
+
+#roof {
+ position: relative;
+ margin-top: 0px;
+ background: #FFF;
+}
+
+#room {
+ padding: 10px 0 10px 0;
+ background-color: #FFF;
+ border-bottom: 1px solid #777;
+}
+
+#CALeft {
+ width: 710px;
+ float: left;
+ position: relative;
+}
+
+#CARight {
+ width: 240px;
+ float: right;
+}
+
+#CAFull {
+ float: left;
+ padding: 0 5px 0 5px;
+ width: 950px;
+}
+
+#ground {
+ width: 100%;
+ border-top: 1px solid #000;
+ padding-top: 6px;
+ padding-bottom: 0px;
+ text-align: center;
+ background: #777;
+}
+
+/*#licenseLogo {position:absolute;top:10px;right:10px;}*/
+
+/*顶部及导航栏*/
+#top {
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ height: 20px;
+ text-align: right;
+ padding: 3px;
+ background-color: #ffffff;
+ width: 500px;
+}
+
+/*#header {width:960px;}*/
+#top a {
+ height: 35px;
+ text-align: right; /*letter-spacing:1px; */
+ margin-left: 20px;
+ text-decoration: underline;
+ font-size: 12px;
+ color: #333333;
+}
+
+#logo {
+ padding: 5px 0px 0px 0px;
+}
+
+#navBar {
+ float: clear;
+ position: relative;
+ display: block;
+ width: 960px;
+}
+
+#navBar .nav {
+ margin: 20px 0px 0px 16px; /*letter-spacing:1px; */
+}
+
+#navBar .nav a {
+ color: #333333;
+ background-color: #fff0e0;
+ /*border-left: 1px solid #eeeeec;
+ border-right: 1px solid #babdb6;
+ border-top: 1px solid #eeeeec;*/
+ border: 1px solid #888888;
+ border-bottom: none;
+ padding: 0px 12px 3px 12px;
+ height: 25px;
+ line-height: 30px;
+ margin-left: 10px;
+ font-size: 14px;
+ font-weight: 400;
+ text-decoration: none;
+ display: block;
+ float: left;
+}
+
+#navBar .nav a:hover {
+ text-decoration: underline
+}
+
+#navBar .nav a.on {
+ height: 24px;
+ line-height: 28px;
+ border-bottom: 1px solid #a40000;
+ border-right: 1px solid #820000;
+ border-top: 1px solid #d40000;
+ border-left: 1px solid #d40000; /*background:#A31E39; */
+ background: #a40000;
+ color: #FFF;
+ font-weight: 600;
+ text-decoration: none
+}
+
+#navBar .nav a.special {
+ font-size: 14px;
+ color: #B02B2C;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+#navBar .nav a.special:hover {
+ text-decoration: underline;
+}
+
+#navBar .nav div.focus {
+ float: right;
+ padding-right: 0px;
+}
+
+/*搜索栏*/
+#searchBar {
+ width: 958px;
+ background-color: #888a85; /*#e9b96e;*/
+ border: 1px solid #aaaaaa;
+ padding: 4px 0 0 0;
+}
+
+/* #B02B2C */
+#searchBar .content {
+}
+
+#searchBar .searchInput {
+ font-size: 13px;
+ height: 18px;
+ width: 400px;
+}
+
+#searchBar .searchBtn {
+ font-size: 14px;
+ height: 26px;
+ width: 80px;
+}
+
+#searchBar .options {
+ padding: 3px 0 3px 0;
+ font-size: 100%;
+ color: #EEE; /*letter-spacing:1px;*/
+}
+
+#searchBar .options INPUT {
+ margin: 0 3px 0 15px;
+}
+
+#searchBar .options INPUT:hover {
+ cursor: pointer
+}
+
+/*问题列表*/
+#listA {
+ float: left;
+ background-color: #FFF;
+ padding: 0 0px 0 0px;
+ width: 100%;
+}
+
+#listA .qstA {
+ position: relative;
+ padding: 3px 5px 5px 10px;
+ border-top: 1px dashed #ccccce;
+ /*border-left:1px solid #ebebbe;
+ border-right:1px solid #b4b48e;
+ border-bottom:1px solid #b4b48e;*/
+ background: white; /* #f9f7ed;*/
+/*margin:10px 0 10px 0;*/
+/*background:url(../images/quest-bg.gif) repeat-x top;*/
+}
+
+#listA .qstA thumb {
+ float: left;
+}
+
+#listA .qstA H2 {
+ font-size: 14px;
+ font-weight: 800;
+ margin: 8px auto;
+ padding: 0px;
+}
+
+#listA .qstA H2 a {
+ color: 333333 /*#2e3436*/;
+ font-size: 15px;
+}
+
+#listA .qstA .stat {
+ position: absolute;
+ right: 0px;
+ bottom: 5px;
+ font-size: 12px; /*letter-spacing:1px;*/
+ float: right;
+}
+
+#listA .qstA .stat span {
+ margin-right: 5px;
+}
+
+#listA .qstA .stat td {
+ min-width: 40px;
+ text-align: center;
+}
+
+#listA .qstA .stat .num {
+ font-family: sans-serif;
+ color: #a40000;
+ /*background:#eeeeec;
+ border: 1px solid #babdb6;*/
+ margin: 0px;
+ font-size: 17px;
+ font-weight: 800;
+}
+
+#listA .qstA table {
+ border-spacing: 0px;
+}
+
+#listA .qstA table td {
+ padding: 0px;
+ width: 60px;
+ text-align: center;
+}
+
+#listA .qstA .stat .unit {
+ color: #777777;
+ margin: 0px
+}
+
+#listA .qstA .from {
+ margin-top: 5px;
+ font-size: 13px;
+ color: #777777;
+}
+
+#listA .qstA .from .score {
+ font-family: sans-serif;
+ color: #555555;
+}
+
+#listA .qstA .date {
+ margin-left: 10px;
+ color: #777777;
+}
+
+#listA .qstA .wiki {
+ color: #763333;
+ font-size: 12px;
+}
+
+#listA .qstA .from a {
+}
+
+#listA .qstA .from IMG {
+ vertical-align: middle;
+}
+
+#listA .qstA .author {
+ font-weight: 400;
+}
+
+#listA .qstA .author a {
+ color: #444444;
+}
+
+#listA .qstA .summary {
+ margin-right: 5px;
+}
+
+.short-summary {
+ position: relative;
+ padding: 3px 5px 5px 10px;
+ border-top: 1px dotted #ccccce;
+ overflow: hidden;
+ width: 700px;
+ float: left;
+}
+
+.short-summary h2 {
+ font-size: 16px;
+ font-family: "Trebuchet MS", "segoe ui", arial, sans-serif;
+}
+
+.short-summary .userinfo {
+ float: right;
+ margin-top: 8px;
+}
+
+.short-summary .counts {
+ float: right;
+ margin-top: 4px;
+}
+
+.short-summary .counts .item-count {
+ font-size: 17px;
+ font-weight: 800;
+}
+
+.short-summary .votes,
+.short-summary .status,
+.short-summary .views {
+ font-size: 12px;
+ text-align: center;
+ margin: 0 0 0 7px;
+ padding: 4px 2px 0px 2px;
+ width: 46px;
+ height: 48px;
+ float: left;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+#question-table {
+ margin-bottom: 10px; /*border-bottom:1px solid #888a85;*/
+}
+
+.evenMore {
+ font-size: 14px;
+ font-weight: 800;
+}
+
+.questions-count {
+ font-size: 32px;
+ font-family: sans-serif;
+ font-weight: 600;
+ padding: 0 0 5px 0px;
+ color: #a40000;
+ margin-top: 3px;
+}
+
+/*内容块*/
+.boxA {
+ background: #888a85;
+ padding: 6px;
+ margin-bottom: 8px;
+ border 1px solid #babdb6;
+}
+
+.boxA H3 {
+ font-size: 13px;
+ font-weight: 800;
+ color: #FFF;
+ margin: 0;
+ padding: 0;
+ margin-bottom: 4px;
+}
+
+.boxA .body {
+ border: 1px solid #999;
+ padding: 8px;
+ background: #FFF;
+ font-size: 13px;
+}
+
+.boxA .more {
+ padding: 2px;
+ text-align: right;
+ font-weight: 800;
+}
+
+.boxB {
+ background: #F9F7ED;
+ padding: 6px;
+ margin-bottom: 8px;
+ border: solid 1px #aaaaaa;
+}
+
+.boxB H3 {
+ font-size: 13px;
+ font-weight: 800;
+ color: #000;
+ margin: 0;
+ padding: 0 0 0 15px;
+ margin-bottom: 4px;
+ background: url(../images/dot-g.gif) no-repeat left center;
+}
+
+.boxB .body {
+ border: 1px solid #aaaaaa;
+ padding: 8px;
+ background: #FFF;
+ font-size: 13px;
+ line-height: 160%;
+}
+
+.boxB .more {
+ padding: 1px;
+ text-align: right;
+ font-weight: 800;
+}
+
+.boxC {
+ background: #cacdc6; /*f9f7ed;*/
+ padding: 10px;
+ margin-bottom: 8px;
+ border-top: 1px solid #eeeeec;
+ border-left: 1px solid #eeeeec;
+ border-right: 1px solid #a9aca5;
+ border-bottom: 1px solid #babdb6;
+}
+
+.boxC p {
+ margin-bottom: 8px;
+}
+
+.boxC p.nomargin {
+ margin: 0px;
+}
+
+.boxC p.info-box-follow-up-links {
+ text-align: right;
+ margin: 0;
+}
+
+/*分页*/
+.pager {
+ margin-top: 10px;
+ margin-bottom: 16px;
+ float: left;
+}
+
+.pagesize {
+ margin-top: 10px;
+ margin-bottom: 16px;
+ float: right;
+}
+
+/** PAGINATOR **/
+.paginator {
+ padding: 5px 0 10px 0;
+ font: normal 12px sans-serif;
+}
+
+.paginator .prev-na,
+.paginator .next-na {
+ padding: .3em;
+ font: bold .875em sans-serif;
+}
+
+.paginator .prev-na,
+.paginator .next-na {
+ border: 1px solid #ccc;
+ background-color: #f9f9f9;
+ color: #aaa;
+ font-weight: normal;
+}
+
+.paginator .prev a, .paginator .prev a:visited,
+.paginator .next a, .paginator .next a:visited {
+ border: 1px solid #fff;
+ background-color: #fff;
+ color: #777;
+ padding: 2px 4px 3px 4px;
+ font: bold 100% sans-serif;
+}
+
+.paginator .prev, .paginator .prev-na {
+ margin-right: .5em;
+}
+
+.paginator .next, .paginator .next-na {
+ margin-left: .5em;
+}
+
+.paginator .page a, .paginator .page a:visited, .paginator .curr {
+ padding: .25em;
+ font: normal .875em verdana;
+ border: 1px solid #ccc;
+ background-color: #fff;
+ margin: 0em .25em;
+ color: #777;
+}
+
+.paginator .curr {
+ background-color: #777;
+ color: #fff;
+ border: 1px solid #777;
+ font-weight: bold;
+}
+
+.paginator .page a:hover,
+.paginator .curr a:hover,
+.paginator .prev a:hover,
+.paginator .next a:hover {
+ color: #fff;
+ background-color: #777;
+ border: 1px solid #777;
+ text-decoration: none;
+}
+
+.paginator .text {
+ color: #777;
+ padding: .3em;
+ font: bold 100% sans-serif;
+}
+
+.paginator-container {
+ float: right;
+ padding: 10px 0 10px 0;
+}
+
+.paginator-container-left {
+ padding: 5px 0 10px 0;
+}
+
+/*标签*/
+.tag {
+ font-size: 13px;
+ font-weight: normal;
+ color: #333;
+ text-decoration: none;
+ background-color: #EEE;
+ border-left: 3px solid #777;
+ border-top: 1px solid #EEE;
+ border-bottom: 1px solid #CCC;
+ border-right: 1px solid #CCC;
+ padding: 1px 8px 1px 8px;
+}
+
+.tags {
+ font-family: sans-serif;
+ line-height: 200%;
+ display: block;
+ margin-top: 5px;
+}
+
+.tags a {
+ white-space: nowrap;
+ font-size: 13px;
+ font-weight: normal;
+ color: #333;
+ text-decoration: none;
+ background-color: #EEE;
+ border-left: 3px solid #777;
+ border-top: 1px solid #EEE;
+ border-bottom: 1px solid #CCC;
+ border-right: 1px solid #CCC;
+ padding: 1px 8px 1px 8px;
+}
+
+.tags a:hover {
+ background-color: #fFF;
+ color: #333;
+}
+
+.tagsbox {
+ line-height: 200%;
+}
+
+.tagsbox a {
+ font-size: 13px;
+ font-weight: normal;
+ color: #333;
+ text-decoration: none;
+ background-color: #EEE;
+ border-left: 3px solid #777;
+ border-top: 1px solid #EEE;
+ border-bottom: 1px solid #CCC;
+ border-right: 1px solid #CCC;
+ padding: 1px 8px 1px 8px;
+}
+
+.tagsbox a:hover {
+ background-color: #fFF;
+ color: #333;
+}
+
+.tag-number {
+ font-weight: 700;
+ font-family: sans-serif;
+}
+
+.marked-tags {
+ margin-top: 0px;
+ margin-bottom: 5px;
+}
+
+.deletable-tag {
+ margin-right: 3px;
+ white-space: nowrap;
+}
+
+/*奖牌*/
+a.medal {
+ font-size: 14px;
+ line-height: 250%;
+ font-weight: 800;
+ color: #333;
+ text-decoration: none;
+ background: url(../images/medala.gif) no-repeat;
+ border-left: 1px solid #EEE;
+ border-top: 1px solid #EEE;
+ border-bottom: 1px solid #CCC;
+ border-right: 1px solid #CCC;
+ padding: 4px 12px 4px 6px;
+}
+
+a:hover.medal {
+ color: #333;
+ text-decoration: none;
+ background: url(../images/medala_on.gif) no-repeat;
+ border-left: 1px solid #E7E296;
+ border-top: 1px solid #E7E296;
+ border-bottom: 1px solid #D1CA3D;
+ border-right: 1px solid #D1CA3D;
+}
+
+/*Tab栏*/
+.tabBar {
+ background-color: #FFF;
+ border-bottom: 1px solid white;
+ height: 30px;
+ width: 100%;
+ clear: both;
+ margin-bottom: 3px;
+}
+
+.tabsA {
+ background-color: #FFF;
+ float: right;
+ position: relative;
+ display: block;
+ font-weight: bold;
+ height: 20px;
+}
+
+.tabsB {
+ background-color: #FFF;
+ float: left;
+ position: relative;
+ display: block;
+ font-weight: bold;
+ height: 20px;
+}
+
+.tabsA a.on, .tabsA a:hover, .tabsB a.on, .tabsB a:hover {
+ background: #fff;
+ color: #a40000;
+ border-top: 1px solid #babdb6;
+ border-left: 1px solid #babdb6;
+ border-right: 1px solid #888a85;
+ border-bottom: 1px solid #888a85;
+ height: 24px;
+ line-height: 26px;
+ margin-top: 3px;
+ padding: 0px 11px 0px 11px;
+}
+
+.tabsA a {
+ background: #f9f7eb;
+ border-top: 1px solid #eeeeec;
+ border-left: 1px solid #eeeeec;
+ border-right: 1px solid #a9aca5;
+ border-bottom: 1px solid #888a85;
+ color: #888a85;
+ display: block;
+ float: left;
+ height: 20px;
+ line-height: 22px;
+ margin: 5px 4px 0 0;
+ padding: 0 11px 0 11px;
+ text-decoration: none;
+}
+
+.tabsB a {
+ background: #eee;
+ border: 1px solid #eee;
+ color: #777;
+ display: block;
+ float: left;
+ height: 22px;
+ line-height: 28px;
+ margin: 5px 0px 0 4px;
+ padding: 0 11px 0 11px;
+ text-decoration: none;
+}
+
+/*.tabsA a:hover, .tabsB a:hover {background: #fff;border: 1px solid #777;border-bottom:3px solid #FFF;}*/
+.headlineA {
+ font-size: 13px;
+ border-bottom: 1px solid #777;
+ padding-bottom: 2px;
+ font-weight: 800;
+ margin-bottom: 12px;
+ text-align: right;
+ height: 30px;
+}
+
+.headQuestions {
+ float: left;
+ height: 23px;
+ line-height: 23px;
+ margin: 5px 0 0 5px;
+ padding: 0px 6px 0px 15px;
+ font-size: 15px;
+ font-weight: 700;
+ border-bottom: 0px solid #777;
+ border-left: 0px solid #darkred;
+ background-color: #FFF;
+ background: url(../images/dot-list.gif) no-repeat left center;
+}
+
+.headAnswers {
+ float: left;
+ padding: 3px;
+ font-size: 18px;
+ font-weight: 800;
+ background: url(../images/ico_answers.gif) left 2px no-repeat;
+ padding-left: 24px;
+}
+
+.headTags {
+ float: left;
+ padding: 3px;
+ font-size: 18px;
+ font-weight: 800;
+ background: url(../images/ico_tags.gif) no-repeat;
+ padding-left: 24px;
+}
+
+.headUsers {
+ float: left;
+ height: 23px;
+ line-height: 23px;
+ margin: 5px 0 0 5px;
+ padding: 0px 6px 0px 15px;
+ font-size: 15px;
+ font-weight: 700;
+ border-bottom: 0px solid #777;
+ border-left: 0px solid #darkred;
+ background-color: #FFF;
+ background: url(../images/dot-list.gif) no-repeat left center;
+}
+
+.headMedals {
+ float: left;
+ height: 23px;
+ line-height: 23px;
+ margin: 5px 0 0 5px;
+ padding: 0px 6px 0px 15px;
+ font-size: 15px;
+ font-weight: 700;
+ border-bottom: 0px solid #777;
+ border-left: 0px solid #darkred;
+ background-color: #FFF;
+ background: url(../images/dot-list.gif) no-repeat left center;
+}
+
+.headLogin {
+ float: left;
+ padding: 3px;
+ font-size: 15px;
+ font-weight: 800;
+ background: url(../images/ico_login.gif) no-repeat;
+ padding-left: 24px;
+}
+
+.headNormal {
+ text-align: left;
+ padding: 3px;
+ font-size: 15px;
+ margin-bottom: 12px;
+ font-weight: bold;
+ border-bottom: 1px solid #777;
+}
+
+.headUser {
+ text-align: left;
+ padding: 5px;
+ font-size: 20px; /*letter-spacing:1px;*/
+ margin-bottom: 12px;
+ font-weight: 800;
+ border-bottom: 1px solid #777;
+}
+
+/*RSS订阅*/
+#feeds {
+ margin: 10px 0;
+}
+
+#feeds a {
+ background: url(../images/feed-icon-small.png) no-repeat 0;
+ padding-left: 18px;
+ font-weight: 700;
+ font-size: 13px;
+}
+
+/*问题*/
+#question {
+ margin-bottom: 30px;
+}
+
+#question h1 {
+ font-size: 15px;
+ background: #CCC;
+ padding: 6px 8px;;
+}
+
+#question .body {
+ background: #F7F7F7;
+ padding: 20px 10px;
+}
+
+.starter {
+ padding: 10px;
+ background: #E0EAF1;
+}
+
+.vote {
+ font-size: 20px;
+ color: #666;
+ font-weight: 800;
+}
+
+.questions-related {
+ font-weight: 700;
+ word-wrap: break-word;
+}
+
+.questions-related p {
+ line-height: 20px;
+ margin-bottom: 10px;
+ font-size: 100%;
+}
+
+.question-status {
+ margin-top: 10px;
+ padding: 20px;
+ background-color: #F5F5F5;
+ text-align: center;
+}
+
+.question-status h3 {
+ font-size: 125%;
+}
+
+.question-body {
+ min-height: 100px;
+ font-size: 13px;
+ line-height: 20px;
+}
+
+.question-body IMG {
+ max-width: 600px;
+}
+
+.question-mark {
+ /*background-color:#fff5e0;
+ border-top: 1px solid #eeeeec;
+ border-right: 1px solid #babdb6;
+ border-bottom: 1px solid #babdb6;
+ border-left: 1px solid #eeeeec;*/
+ text-align: left;
+ padding: 5px;
+ overflow: hidden;
+}
+
+.question-edit {
+ text-align: left;
+ overflow: hidden;
+}
+
+.vote-buttons {
+ float: left;
+ text-align: center;
+}
+
+.vote-buttons IMG {
+ cursor: pointer;
+}
+
+.vote-number {
+ font-family: Arial;
+ padding: 0px 0 3px 0;
+ font-size: 140%;
+ font-weight: bold;
+ color: #777;
+}
+
+.question-img-upvote:hover {
+ background: url(../images/vote-arrow-up-on.png)
+}
+
+.question-img-downvote:hover {
+ background: url(../images/vote-arrow-down-on.png)
+}
+
+.question-img-favorite:hover {
+ background: url(../images/vote-favorite-on.png)
+}
+
+.favorite-number {
+ padding: 0px;
+ font-size: 100%;
+ font-family: Arial;
+ font-weight: bold;
+ color: #777;
+}
+
+.vote-notification {
+ z-index: 1;
+ cursor: pointer;
+ display: none;
+ position: absolute;
+ padding: 15px;
+ color: White;
+ background-color: darkred;
+ text-align: center;
+}
+
+.vote-notification a {
+ color: White;
+ text-decoration: underline;
+}
+
+.offensive-flag a {
+ color: #777;
+ padding: 3px;
+ cursor: pointer;
+}
+
+.offensive-flag a:hover {
+ background-color: #777;
+ text-decoration: none;
+ color: #fff;
+}
+
+.linksopt a {
+ color: #777;
+ padding: 3px;
+ cursor: pointer;
+}
+
+.linksopt a:hover {
+ background-color: #777;
+ text-decoration: none;
+ color: #fff;
+}
+
+.action-link a {
+ color: #777;
+ padding: 3px;
+ cursor: pointer;
+}
+
+.action-link: a hover {
+ background-color: #777;
+ text-decoration: none;
+ color: #fff;
+}
+
+.action-link-separator {
+ color: #ccc;
+}
+
+.wiki-category {
+ margin-left: 5px;
+ color: #999;
+ font-size: 90%;
+}
+
+div.comments {
+ line-height: 150%;
+ padding: 10px 0;
+}
+
+div.post-comments {
+ clear: both;
+ background: url(../images/gray-up-arrow-h18px.png) no-repeat;
+ width: 100%;
+ padding-left: 12px;
+ margin: 3px 0 10px 0;
+}
+
+form.post-comments textarea {
+ height: 6em;
+ margin-bottom: 4px;
+}
+
+form.post-comments input {
+ margin-left: 10px;
+ margin-top: 1px;
+ vertical-align: top;
+ width: 100px;
+}
+
+span.text-counter {
+ margin-right: 20px;
+ font-size: 11px;
+}
+
+span.form-error {
+ color: #990000;
+ font-weight: normal;
+ margin-left: 5px;
+}
+
+p.form-item {
+ margin: 0px;
+}
+
+div.comments-container, div.comments-container-accepted, div.comments-container-owner, div.comments-container-deleted {
+ padding: 0;
+}
+
+.post-comments a {
+ color: #888888;
+ padding: 0 3px 2px;
+}
+
+a.comments-link, a.comments-link-accepted, a.comments-link-owner, a.comments-link-deleted {
+ color: black;
+ font-size: 11px;
+ background: #eeeeee;
+ padding: 3px;
+ cursor: pointer;
+}
+
+.post-comments a:hover {
+ background-color: #777777;
+ color: white;
+ text-decoration: none;
+}
+
+a.comment-user, a.comment-user:hover {
+ background-color: inherit;
+ color: blue;
+ padding: 0;
+}
+
+a.comment-user:hover {
+ text-decoration: underline;
+}
+
+/*回答*/
+#answers {
+}
+
+.answer {
+ padding-top: 10px;
+ width: 100%;
+ border-bottom: 1px solid #ccccce;
+}
+
+.answer-body {
+ min-height: 80px;
+ font-size: 13px;
+ line-height: 20px;
+}
+
+.answer-body IMG {
+ max-width: 600px;
+}
+
+.accepted-answer {
+ background-color: #EBFFE6;
+ border-bottom-color: #9BD59B;
+}
+
+.accepted-answer .comments-link {
+ background-color: #CCFFBF;
+}
+
+.accepted-answer .comments-container {
+ background-color: #CCFFBF;
+}
+
+.answered {
+ background: #CCC;
+ color: #999;
+}
+
+.answered-accepted {
+ background: #CCC;
+ color: #763333;
+}
+
+.unanswered {
+ background: #777;
+ color: white;
+}
+
+.answered-by-owner {
+ background: #E9E9FF;
+}
+
+.answered-by-owner .comments-link {
+ background-color: #E6ECFF;
+}
+
+.answered-by-owner .comments-container {
+ background-color: #E6ECFF;
+}
+
+.answered-accepted strong {
+ color: #E1E818;
+}
+
+.answer-img-accept:hover {
+ background: url(../images/vote-accepted-on.png)
+}
+
+.deleted {
+ background: #F4E7E7 none repeat scroll 0 0;
+}
+
+/*标签列表*/
+/*
+.tagsbox {}
+.tagsbox a {color:#000;line-height:30px;margin-right:10px;font-size:100%;background-color:#F9F7ED;padding:3px;border:1px solid #aaaaaa;}
+.tagsbox a:hover {text-decoration:none;background-color:#F9F7ED;color:#B02B2C;} */
+.tagsList {
+ margin: 0;
+ list-style-type: none;
+ padding: 0px;
+ min-height: 360px;
+}
+
+.tagsList li {
+ width: 235px;
+ float: left;
+}
+
+.badge-list {
+ margin: 0;
+ list-style-type: none;
+}
+
+/*登录*/
+.list-item {
+ margin-left: 15px;
+}
+
+.list-item LI {
+ list-style-type: disc;
+ font-size: 13px;
+ line-height: 20px;
+ margin-bottom: 10px;
+}
+
+/* openid styles */
+.form-row {
+ line-height: 25px;
+}
+
+table.form-as-table {
+ margin-top: 5px;
+}
+
+table.form-as-table ul {
+ list-style-type: none;
+ display: inline;
+}
+
+table.form-as-table li {
+ display: inline;
+}
+
+table.form-as-table td {
+ text-align: right;
+}
+
+table.form-as-table th {
+ text-align: left;
+ font-weight: normal;
+}
+
+/*.form-row li label {
+ display: inline
+}*/
+.submit-row {
+ line-height: 30px;
+ padding-top: 10px;
+ display: block;
+ clear: both;
+}
+
+.errors {
+ line-height: 20px;
+ color: red;
+}
+
+.error {
+ color: darkred;
+ margin: 0;
+ font-size: 10px;
+}
+
+.error-list li {
+ padding: 5px;
+}
+
+.fieldset {
+/* border:solid 1px #777;*/
+ border: none;
+ margin-top: 10px;
+ padding: 10px;
+}
+
+.openid-input {
+ background: url(../images/openid.gif) no-repeat;
+ padding-left: 15px;
+ cursor: pointer;
+}
+
+.openid-login-input {
+ background-position: center left;
+ background: url(../images/openid.gif) no-repeat 0% 50%;
+ padding: 5px 5px 5px 15px;
+ cursor: pointer;
+ font-family: Trebuchet MS;
+ font-weight: 300;
+ font-size: 150%;
+ width: 500px;
+}
+
+.openid-login-submit {
+ height: 40px;
+ width: 80px;
+ line-height: 40px;
+ cursor: pointer;
+ border: 1px solid #777;
+ font-weight: bold;
+ font-size: 120%;
+}
+
+.openid-samples {
+
+}
+
+.openid-samples .list, .list li {
+ font-family: Trebuchet MS, "segoe ui", Helvetica, "Microsoft YaHei", 宋 体, Tahoma, Verdana, MingLiu, PMingLiu, Arial, sans-serif;
+ list-style: none !important;
+ margin-left: -30px !important;
+ line-height: 20px !important;
+}
+
+/*表单相关*/
+span.form-error {
+ color: #990000;
+ font-size: 90%;
+ font-weight: normal;
+ margin-left: 5px;
+}
+
+.title-desc {
+ color: #666666;
+ font-size: 90%;
+}
+
+/*adjustment for editor preview*/
+#editor {
+ font-size: 100%;
+ min-height: 200px;
+ line-height: 18px;
+ width: 100%;
+}
+
+.wmd-preview {
+ margin-top: 10px;
+ padding: 6px;
+ width: 100%;
+ background-color: #F5F5F5;
+ min-height: 20px;
+}
+
+.wmd-preview pre {
+ background-color: #E7F1F8;
+
+}
+
+.wmd-preview blockquote {
+ background-color: #eee;
+}
+
+.wmd-preview IMG {
+ max-width: 600px;
+}
+
+.preview-toggle {
+ font-weight: 600;
+ width: 100%;
+ color: #aaa; /*letter-spacing:1px;*/
+ text-align: left;
+}
+
+.preview-toggle span:hover {
+ cursor: pointer;
+}
+
+.edit-content-html {
+ border-top: 1px dotted #D8D2A9;
+ border-bottom: 1px dotted #D8D2A9;
+ margin: 5px 0 5px 0;
+}
+
+/*修订记录*/
+
+#revisions {
+ width: 950px;
+}
+
+.revision {
+ margin: 10px 0 10px 0;
+ width: 100%;
+ font-size: 13px;
+}
+
+.revision .header {
+ background-color: #eee;
+ padding: 5px;
+ cursor: pointer;
+}
+
+.revision .author {
+ background-color: #E9E9FF;
+}
+
+.revision .summary {
+ padding: 5px 0 10px 0;
+}
+
+.revision .summary span {
+ background-color: yellow;
+ padding-left: 3px;
+ padding-right: 3px;
+ display: inline;
+}
+
+.revision h1 {
+ font-size: 130%;
+ font-weight: 600;
+ padding: 15px 0 15px 0;
+}
+
+.revision-mark {
+ width: 200px;
+ text-align: left;
+ display: inline-block;
+ font-size: 90%;
+ overflow: hidden;
+}
+
+.revision-number {
+ font-size: 300%;
+ font-weight: bold;
+ font-family: sans-serif;
+}
+
+.revision .body {
+ padding-left: 10px;
+ margin-bottom: 50px;
+}
+
+.revision .answerbody {
+ padding: 10px 0 5px 10px;
+}
+
+/* Revision pages */
+del {
+ color: #FF5F5F;
+}
+
+del .post-tag {
+ color: #FF5F5F;
+}
+
+ins {
+ background-color: #97ff97;
+}
+
+ins .post-tag {
+ background-color: #97ff97;
+}
+
+/*用户资料页面*/
+.count {
+ font-family: Arial;
+ font-size: 200%;
+ font-weight: 700;
+ color: #777
+}
+
+.scoreNumber {
+ font-family: Arial;
+ font-size: 35px;
+ font-weight: 800;
+ color: #777;
+ line-height: 40px; /*letter-spacing:0px*/
+}
+
+.user-details {
+ font-size: 13px;
+}
+
+.user-about {
+ background-color: #EEEEEE;
+ height: 200px;
+ line-height: 20px;
+ overflow: auto;
+ padding: 10px;
+ width: 90%;
+}
+
+.user-edit-link {
+ background: url(../images/edit.png) no-repeat;
+ padding-left: 20px;
+}
+
+.favorites-count-off {
+ color: #919191;
+ float: left;
+ padding: 3px;
+ margin: 10px 0 0 0;
+ text-align: center;
+}
+
+.favorites-count {
+ color: #D4A849;
+ float: left;
+ padding: 3px;
+ margin: 10px 0 0 0;
+ text-align: center;
+}
+
+.favorites-empty {
+ width: 32px;
+ height: 45px;
+ float: left;
+}
+
+.question-summary {
+ border-bottom: 1px dotted #999999;
+ float: left;
+ overflow: hidden;
+ padding: 11px 0;
+ width: 670px;
+}
+
+.user-info-table {
+ width: 950;
+ margin-bottom: 10px;
+}
+
+.user-stats-table .question-summary {
+ width: 800px;
+}
+
+.narrow .stats {
+ background: transparent none repeat scroll 0 0;
+ float: left;
+ height: 48px;
+ margin: 0 0 0 7px;
+ padding: 0;
+ width: auto;
+ font-family: Arial;
+}
+
+.stats div {
+ font-size: 11px;
+ text-align: center;
+}
+
+.narrow .votes {
+ background: #EEEEEE none repeat scroll 0 0;
+ float: left;
+ height: 42px;
+ margin: 0 3px 0 0;
+ padding: 5px;
+ width: 46px;
+ text-align: center;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+.narrow .summary {
+ width: 600px;
+ display: inline-block;
+}
+
+.narrow .summary h3 {
+ padding: 0px;
+ margin: 0px;
+}
+
+.narrow .views {
+ height: 42px;
+ float: left;
+ margin: 0 7px 0 0; /*padding:5px 0 5px 4px;*/
+ padding: 5px;
+ width: 46px;
+ text-align: center;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ color: #777;
+}
+
+.narrow .status {
+ float: left;
+ height: 42px;
+ margin: 0 3px 0 0;
+ padding: 5px;
+ width: 46px;
+ text-align: center;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+.narrow .vote-count-post {
+ font-weight: 800;
+ display: block;
+ margin: 0;
+ font-size: 190%;
+ color: #555;
+ line-height: 20px;
+}
+
+.narrow .answer-count-post {
+ font-weight: 800;
+ display: block;
+ margin: 0;
+ font-size: 190%;
+}
+
+.narrow .views-count-post {
+ font-weight: 800;
+ display: block;
+ margin: 0;
+ font-size: 190%;
+}
+
+div.started {
+ color: #999999;
+ float: right;
+ line-height: 18px;
+
+}
+
+.narrow div.started {
+ line-height: inherit;
+ padding-top: 4px;
+ white-space: nowrap;
+ width: auto;
+}
+
+.relativetime {
+ font-weight: bold;
+ text-decoration: none;
+}
+
+div.started a {
+ font-weight: bold;
+}
+
+div.started .reputation-score {
+ margin-left: 1px;
+}
+
+.narrow .tags {
+ float: left;
+}
+
+.answer-summary {
+ display: block;
+ clear: both;
+ padding: 3px;
+}
+
+.answer-votes {
+ background-color: #EEEEEE;
+ color: #555555;
+ float: left;
+ font-family: Arial;
+ font-size: 110%;
+ font-weight: bold;
+ height: 15px;
+ padding: 4px 4px 5px;
+ text-align: center;
+ text-decoration: none;
+ width: 20px;
+ margin-right: 10px;
+}
+
+.vote-count {
+ font-family: Arial;
+ font-size: 160%;
+ font-weight: 700;
+ color: #777;
+}
+
+.user-action {
+
+}
+
+.user-action-1 {
+ font-weight: bold;
+ color: #333;
+}
+
+.user-action-2 {
+ font-weight: bold;
+ color: #CCC;
+}
+
+.user-action-3 {
+ color: #333;
+}
+
+.user-action-4 {
+ color: #333;
+}
+
+.user-action-5 {
+ color: darkred;
+}
+
+.user-action-6 {
+ color: darkred;
+}
+
+.user-action-7 {
+ color: #333;
+}
+
+.user-action-8 {
+ padding: 3px;
+ font-weight: bold;
+ background-color: #CCC;
+ color: #763333;
+}
+
+.revision-summary {
+ background-color: #FFFE9B;
+ padding: 2px;
+}
+
+.question-title-link a {
+ font-weight: bold;
+ color: #0077CC;
+}
+
+.answer-title-link a {
+ color: #333;
+}
+
+.post-type-1 a {
+ font-weight: bold;
+
+}
+
+.post-type-3 a {
+ font-weight: bold;
+
+}
+
+.post-type-5 a {
+ font-weight: bold;
+}
+
+.post-type-2 a {
+ color: #333;
+}
+
+.post-type-4 a {
+ color: #333;
+}
+
+.post-type-6 a {
+ color: #333;
+}
+
+.post-type-8 a {
+ color: #333;
+}
+
+/*读书频道*/
+.bookInfo {
+ float: left;
+ width: 940px;
+ padding: 5px;
+}
+
+.bookCover {
+ float: left;
+ width: 200px;
+}
+
+.bookCover img {
+ border: 1px solid #ccc;
+ max-width: 200px;
+}
+
+.bookSummary {
+ float: left;
+ font-size: 13px;
+}
+
+.blogRss {
+ float: right;
+ margin: 0 10px 0 0;
+ width: 460px;
+ height: 240px;
+ background-color: #EEE;
+ padding: 5px;
+}
+
+.bookQuestions {
+ margin-bottom: 10px;
+}
+
+.bookFeed {
+ float: right;
+}
+
+.bookAsk {
+/*letter-spacing:1px; */
+ float: right;
+ margin: -30px 10px 0 0;
+ padding: 3px 5px 3px 5px;
+}
+
+.bookAsk a {
+ font-size: 15px;
+ color: #FFF;
+ font-weight: bold;
+ text-decoration: none;
+ background-color: #EC7000;
+ padding: 3px 6px 3px 6px;
+}
+
+.bookAsk a:hover {
+ text-decoration: underline;
+}
+
+/*其他全局样式*/
+.hilite {
+ background-color: #ff0;
+}
+
+.hilite1 {
+ background-color: #ff0;
+}
+
+.hilite2 {
+ background-color: #f0f;
+}
+
+.hilite3 {
+ background-color: #0ff;
+}
+
+.userStatus {
+ margin-left: 12px;
+ color: #FFF;
+ float: right;
+}
+
+.userStatus a {
+ color: #FFF;
+}
+
+.gold, .badge1 {
+ color: #FFCC00;
+}
+
+.silver, .badge2 {
+ color: #CCCCCC;
+}
+
+.bronze, .badge3 {
+ color: #CC9933;
+}
+
+.score {
+ font-weight: 800;
+ color: #333;
+}
+
+.footerLinks {
+ color: #EEE;
+ font-size: 13px; /* letter-spacing:1px;*/
+}
+
+.footerLinks a {
+ color: #FFF;
+ font-size: 13px;
+}
+
+.subSearch {
+ margin-bottom: 12px;
+ padding: 4px;
+}
+
+a.comment {
+ background: #EEE;
+ color: #993300;
+ padding: 4px;
+}
+
+a.permLink {
+ padding: 2px;
+}
+
+a.offensive {
+ color: #999;
+}
+
+ul.bulleta li {
+ background: url(../images/bullet_green.gif) no-repeat 0px 2px;
+ padding-left: 16px;
+ margin-bottom: 4px;
+}
+
+.user {
+ padding: 5px;
+ line-height: 140%;
+ width: 170px;
+}
+
+.user ul {
+ margin: 0;
+ list-style-type: none;
+}
+
+.user .thumb {
+ clear: both;
+ float: left;
+ margin-right: 4px;
+ display: inline;
+}
+
+.yellowbg {
+ background: yellow;
+}
+
+.message {
+ padding: 5px;
+ margin: 10px 0 10px 0;
+ background-color: #eee;
+ border: 1px solid #aaaaaa;
+}
+
+.message h1 {
+ padding-top: 0px;
+ font-size: 15px;
+}
+
+.message p {
+ margin-bottom: 0px;
+}
+
+p.space-above {
+ margin-top: 10px;
+}
+
+.warning {
+ color: red;
+}
+
+.darkred {
+ color: darkred;
+}
+
+.submit {
+ cursor: pointer; /*letter-spacing:1px;*/
+ background-color: #D4D0C8;
+ height: 40px;
+ border: 1px solid #777777; /* width:100px; */
+ font-weight: bold;
+ padding-bottom: 4px;
+ font-size: 120%;
+}
+
+.submit:hover {
+ text-decoration: underline;
+}
+
+.ask-body {
+ padding-right: 10px;
+}
+
+.thousand {
+ color: orange;
+}
+
+.notify {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ z-index: 100;
+ padding: 0;
+ text-align: center;
+ font-weight: Bold;
+ color: #444;
+ background-color: #F4A83D;
+}
+
+.notify p {
+ margin-top: 5px;
+ margin-bottom: 5px;
+ font-size: 16px;
+}
+
+#close-notify {
+ position: absolute;
+ right: 5px;
+ top: 5px;
+ padding: 0 3px 0 3px;
+ color: #735005;
+ text-decoration: none;
+ font-size: 14px;
+ line-height: 18px;
+ background-color: #FAD163;
+ border: 2px #735005 solid;
+ cursor: pointer;
+}
+
+#close-notify:hover {
+ text-decoration: none;
+}
+
+.big {
+ font-size: 15px;
+}
+
+.bigger {
+ font-size: 14px;
+}
+
+.strong {
+ font-weight: bold;
+}
+
+.orange {
+ color: #d64000;
+ font-weight: bold;
+}
+
+.grey {
+ color: #808080;
+}
+
+.about div {
+ padding: 10px 5px 10px 5px;
+ border-top: 1px dashed #aaaaaa;
+}
+
+.about div.first {
+ padding-top: 0;
+ border-top: none;
+}
+
+.about p {
+ margin-bottom: 10px;
+}
+
+.about a {
+ color: #d64000;
+ text-decoration: underline;
+}
+
+.about h3 {
+ line-height: 30px;
+ font-size: 15px;
+ font-weight: 700;
+ padding-top: 0px;
+}
+
+.highlight {
+ background-color: #FFF8C6;
+}
+
+.nomargin {
+ margin: 0;
+}
+
+.margin-bottom {
+ margin-bottom: 10px;
+}
+
+.margin-top {
+ margin-top: 10px;
+}
+
+.inline-block {
+ display: inline-block;
+}
+
+.action-status {
+ margin: 0;
+ border: none;
+ text-align: center;
+ line-height: 10px;
+ font-size: 12px;
+ padding: 0;
+}
+
+.action-status span {
+ padding: 3px 5px 3px 5px;
+ background-color: #fff380; /* nice yellow */
+ font-weight: normal;
+ -moz-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ -webkit-border-radius: 5px;
+}
+
+.tight {
+ margin: 0;
+ padding: 0;
+}
+
+.list-table td {
+ vertical-align: top;
+}
+
+p.comment {
+ border-top: 1px dotted #ccccce;
+ margin: 0;
+ font-size: 11px;
+ color: #444444;
+ padding: 5px 0 5px 0;
+}
+
+.delete-icon {
+ vertical-align: middle;
+ padding-left: 3px;
+}
+
+/* these need to go */
+table.form-as-table .errorlist {
+ display: block;
+ margin: 0;
+ padding: 0 0 0 5px;
+ text-align: left;
+ font-size: 10px;
+ color: darkred;
+}
+
+table.form-as-table input {
+ display: inline;
+ margin-left: 4px;
+}
+
+table.form-as-table th {
+ vertical-align: bottom;
+ padding-bottom: 4px;
+}
+
+.form-row-vertical {
+ margin-top: 8px;
+ display: block;
+}
+
+.form-row-vertical label {
+ margin-bottom: 3px;
+ display: block;
+}
+
+/* above stuff needs to go */
+.text-align-right {
+ text-align: center;
+}
+
+ul.form-horizontal-rows {
+ list-style: none;
+ margin: 0;
+}
+
+ul.form-horizontal-rows li {
+ position: relative;
+ height: 40px;
+}
+
+ul.form-horizontal-rows label {
+ display: inline-block;
+}
+
+ul.form-horizontal-rows ul.errorlist {
+ list-style: none;
+ color: darkred;
+ font-size: 10px;
+ line-height: 10px;
+ position: absolute;
+ top: 2px;
+ left: 180px;
+ text-align: left;
+ margin: 0;
+}
+
+ul.form-horizontal-rows ul.errorlist li {
+ height: 10px;
+}
+
+ul.form-horizontal-rows label {
+ position: absolute;
+ left: 0px;
+ bottom: 6px;
+ margin: 0px;
+ line-height: 12px;
+ font-size: 12px;
+}
+
+ul.form-horizontal-rows li input {
+ position: absolute;
+ bottom: 0px;
+ left: 180px;
+ margin: 0px;
+}
+
+#emailpw-form li input {
+ left: 170px;
+}
+
+#emailpw-form ul.errorlist {
+ left: 170px;
+}
+
+#changepw-form li input {
+ left: 150px;
+}
+
+#changepw-form ul.errorlist {
+ left: 150px;
+}
+
+.narrow .summary {
+ float: left;
+}
+
+.narrow .summary .question-title {
+ font-weight: bold;
+ font-size: 120%;
+}
+
+.user-profile-tool-links {
+ padding-bottom: 10px;
+ font-weight: bold;
+}
+
+.post-controls {
+ float: left;
+ font-size: 11px;
+ line-height: 12px;
+ min-width: 200px;
+ margin-bottom: 5px;
+}
+
+#question-controls .tags {
+ margin: 0 0 3px 0;
+}
+
+.post-update-info-container {
+ float: right;
+ min-width: 190px;
+}
+
+.post-update-info {
+ display: inline-block;
+ float: right;
+ width: 190px;
+ margin-bottom: 5px;
+}
+
+.post-update-info p {
+ font-size: 11px;
+ line-height: 15px;
+ margin: 0 0 4px 0;
+ padding: 0;
+}
+
+.post-update-info img {
+ float: left;
+ width: 32px;
+ margin: 4px 8px 0 0;
+}
+
+.comments-container {
+ clear: both;
+}
+
+.admin {
+ background-color: #fff380; /* nice yellow */
+ border: 1px solid darkred;
+ padding: 0 5px 0 5px;
+}
+
+.admin p {
+ margin-bottom: 3px;
+}
+
+.admin #action_status {
+ text-align: center;
+ font-weight: bold;
+}
+
+#tagSelector {
+ padding-bottom: 2px;
+}
+
+#hideIgnoredTagsControl {
+ margin: 5px 0 0 0;
+}
+
+#hideIgnoredTagsCb {
+ margin: 0 2px 0 1px;
+}
+
+#recaptcha_widget_div {
+ width: 318px;
+ float: left;
+ clear: both;
+}
+
+p.signup_p {
+ margin: 20px 0px 0px 0px;
+}
+
+.simple-subscribe-options ul {
+ list-style: none;
+ list-style-position: outside;
+ margin: 0;
+}
diff --git a/templates/404.html b/forum/skins/default/templates/404.html
index 227de3ae..227de3ae 100644
--- a/templates/404.html
+++ b/forum/skins/default/templates/404.html
diff --git a/templates/500.html b/forum/skins/default/templates/500.html
index 51e73178..51e73178 100644
--- a/templates/500.html
+++ b/forum/skins/default/templates/500.html
diff --git a/templates/about.html b/forum/skins/default/templates/about.html
index 66dcc3fd..66dcc3fd 100644
--- a/templates/about.html
+++ b/forum/skins/default/templates/about.html
diff --git a/templates/answer_edit.html b/forum/skins/default/templates/answer_edit.html
index cd247a3c..2d736f30 100644
--- a/templates/answer_edit.html
+++ b/forum/skins/default/templates/answer_edit.html
@@ -4,12 +4,12 @@
{% load extra_tags %}
{% block title %}{% spaceless %}{% trans "Edit answer" %}{% endspaceless %}{% endblock %}
{% block forejs %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.validate.pack.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/wmd/showdown.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/wmd/wmd.js" %}'></script>
- <link rel="stylesheet" type="text/css" href="{% href "/content/js/wmd/wmd.css" %}" />
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.post.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.validate.pack.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/wmd/showdown.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/wmd/wmd.js" %}'></script>
+ <link rel="stylesheet" type="text/css" href="{% media "/media/js/wmd/wmd.css" %}" />
<script type="text/javascript">
$().ready(function(){
diff --git a/templates/answer_edit_tips.html b/forum/skins/default/templates/answer_edit_tips.html
index c390da06..c390da06 100644
--- a/templates/answer_edit_tips.html
+++ b/forum/skins/default/templates/answer_edit_tips.html
diff --git a/templates/ask.html b/forum/skins/default/templates/ask.html
index 30d43ee0..083b01d9 100644
--- a/templates/ask.html
+++ b/forum/skins/default/templates/ask.html
@@ -4,12 +4,12 @@
{% load extra_tags %}
{% block title %}{% spaceless %}{% trans "Ask a question" %}{% endspaceless %}{% endblock %}
{% block forejs %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.validate.pack.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/wmd/showdown.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/wmd/wmd.js" %}'></script>
- <link rel="stylesheet" type="text/css" href="{% href "/content/js/wmd/wmd.css" %}" />
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.post.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.validate.pack.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/wmd/showdown.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/wmd/wmd.js" %}'></script>
+ <link rel="stylesheet" type="text/css" href="{% media "/media/js/wmd/wmd.css" %}" />
<script type="text/javascript">
$().ready(function(){
//set current module button style
diff --git a/templates/authopenid/changeemail.html b/forum/skins/default/templates/authopenid/changeemail.html
index 94d1881c..94d1881c 100644
--- a/templates/authopenid/changeemail.html
+++ b/forum/skins/default/templates/authopenid/changeemail.html
diff --git a/templates/authopenid/changeopenid.html b/forum/skins/default/templates/authopenid/changeopenid.html
index d01788fb..d01788fb 100644
--- a/templates/authopenid/changeopenid.html
+++ b/forum/skins/default/templates/authopenid/changeopenid.html
diff --git a/templates/authopenid/changepw.html b/forum/skins/default/templates/authopenid/changepw.html
index 8b059544..8b059544 100644
--- a/templates/authopenid/changepw.html
+++ b/forum/skins/default/templates/authopenid/changepw.html
diff --git a/templates/authopenid/complete.html b/forum/skins/default/templates/authopenid/complete.html
index c967e8e2..62970e38 100644
--- a/templates/authopenid/complete.html
+++ b/forum/skins/default/templates/authopenid/complete.html
@@ -95,6 +95,9 @@ parameters:
<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>
diff --git a/templates/authopenid/confirm_email.txt b/forum/skins/default/templates/authopenid/confirm_email.txt
index 3a01f146..3a01f146 100644
--- a/templates/authopenid/confirm_email.txt
+++ b/forum/skins/default/templates/authopenid/confirm_email.txt
diff --git a/templates/authopenid/delete.html b/forum/skins/default/templates/authopenid/delete.html
index 0f9f1c60..0f9f1c60 100644
--- a/templates/authopenid/delete.html
+++ b/forum/skins/default/templates/authopenid/delete.html
diff --git a/templates/authopenid/email_validation.txt b/forum/skins/default/templates/authopenid/email_validation.txt
index 5b166a9b..5b166a9b 100644
--- a/templates/authopenid/email_validation.txt
+++ b/forum/skins/default/templates/authopenid/email_validation.txt
diff --git a/templates/authopenid/external_legacy_login_info.html b/forum/skins/default/templates/authopenid/external_legacy_login_info.html
index 3318499c..3318499c 100644
--- a/templates/authopenid/external_legacy_login_info.html
+++ b/forum/skins/default/templates/authopenid/external_legacy_login_info.html
diff --git a/templates/authopenid/failure.html b/forum/skins/default/templates/authopenid/failure.html
index d075d6b0..d075d6b0 100644
--- a/templates/authopenid/failure.html
+++ b/forum/skins/default/templates/authopenid/failure.html
diff --git a/templates/authopenid/sendpw.html b/forum/skins/default/templates/authopenid/sendpw.html
index 6241c811..6241c811 100644
--- a/templates/authopenid/sendpw.html
+++ b/forum/skins/default/templates/authopenid/sendpw.html
diff --git a/templates/authopenid/sendpw_email.txt b/forum/skins/default/templates/authopenid/sendpw_email.txt
index f044ca45..f044ca45 100644
--- a/templates/authopenid/sendpw_email.txt
+++ b/forum/skins/default/templates/authopenid/sendpw_email.txt
diff --git a/templates/authopenid/settings.html b/forum/skins/default/templates/authopenid/settings.html
index 66ea5953..66ea5953 100644
--- a/templates/authopenid/settings.html
+++ b/forum/skins/default/templates/authopenid/settings.html
diff --git a/templates/authopenid/signin.html b/forum/skins/default/templates/authopenid/signin.html
index 51b8aa7f..4e060d0f 100755
--- a/templates/authopenid/signin.html
+++ b/forum/skins/default/templates/authopenid/signin.html
@@ -4,10 +4,10 @@
{% load extra_tags %}
{% block title %}{% spaceless %}{% trans "User login" %}{% endspaceless %}{% endblock %}
{% block forejs %}
- <script type='text/javascript' src='{% href "/content/js/jquery.validate.pack.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.validate.pack.js" %}'></script>
- <link rel="stylesheet" type="text/css" media="screen" href="{% href "/content/jquery-openid/openid.css" %}"/>
- <script type="text/javascript" src="{% href "/content/jquery-openid/jquery.openid.js" %}"></script>
+ <link rel="stylesheet" type="text/css" media="screen" href="{% media "/media/jquery-openid/openid.css" %}"/>
+ <script type="text/javascript" src="{% media "/media/jquery-openid/jquery.openid.js" %}"></script>
<script type="text/javascript"> $().ready( function() { $("form.openid:eq(0)").openid(); })</script>
<!--<script type="text/javascript">
$().ready(function(){
@@ -44,34 +44,34 @@
<ul class="providers">
<li class="local" title="Local login">
<div class="logo_box local_login_box">
- <img src="{% href "/content/jquery-openid/images/local-login.png" %}" alt="your icon here" />
+ <img src="{% media "/media/jquery-openid/images/local-login.png" %}" alt="your icon here" />
</div>
<span></span>
</li>
<li class="direct" title="Google">
<div class="logo_box google_box">
- <img src="{% href "/content/jquery-openid/images/google.gif" %}" alt="icon" /><span>https://www.google.com/accounts/o8/id</span>
+ <img src="{% media "/media/jquery-openid/images/google.gif" %}" alt="icon" /><span>https://www.google.com/accounts/o8/id</span>
</div>
</li>
<li class="direct" title="Yahoo">
<div class="logo_box yahoo_box">
- <img src="{% href "/content/jquery-openid/images/yahoo.gif" %}" alt="icon" /><span>http://yahoo.com/</span>
+ <img src="{% media "/media/jquery-openid/images/yahoo.gif" %}" alt="icon" /><span>http://yahoo.com/</span>
</div>
</li>
<li class="username" title="AOL screen name">
<div class="logo_box aol_box">
- <img src="{% href "/content/jquery-openid/images/aol.gif" %}" alt="icon" /><span>http://openid.aol.com/<strong>username</strong></span>
+ <img src="{% media "/media/jquery-openid/images/aol.gif" %}" alt="icon" /><span>http://openid.aol.com/<strong>username</strong></span>
</div>
</li>
</ul>
<ul id="openid_small_providers" class="providers">
<!--<li class="openid" title="OpenID">
<div class="logo_box openid_box">
- <img src="/content/jquery-openid/images/openid.gif" alt="icon" />
+ <img src="/media/jquery-openid/images/openid.gif" alt="icon" />
</div>
<span><strong>http://{your-openid-url}</strong></span>
</li>-->
- <li class="direct first_tiny_li facebook" title="Facebook Connect">
+ <li class="first_tiny_li facebook" title="Facebook Connect">
{% if question %}
<fb:login-button onlogin="window.location = '{% url fb_signin_new_question %}'"></fb:login-button>
{% else %}
@@ -83,43 +83,43 @@
{% endif %}
</li>
<li class="openid" title="OpenID URL">
- <img src="{% href "/content/jquery-openid/images/openidico16.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/openidico16.png" %}" alt="icon" />
<span>http://{your-openid-url}</span>
</li>
<li class="username" title="MyOpenID user name">
- <img src="{% href "/content/jquery-openid/images/myopenid-2.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/myopenid-2.png" %}" alt="icon" />
<span>http://<strong>username</strong>.myopenid.com/</span>
</li>
<li class="username" title="Flickr user name">
- <img src="{% href "/content/jquery-openid/images/flickr.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/flickr.png" %}" alt="icon" />
<span>http://flickr.com/<strong>username</strong>/</span>
</li>
<li class="username" title="Technorati user name">
- <img src="{% href "/content/jquery-openid/images/technorati-1.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/technorati-1.png" %}" alt="icon" />
<span>http://technorati.com/people/technorati/<strong>username</strong>/</span>
</li>
<li class="username" title="Wordpress blog name">
- <img src="{% href "/content/jquery-openid/images/wordpress.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/wordpress.png" %}" alt="icon" />
<span>http://<strong>username</strong>.wordpress.com</span>
</li>
<li class="username" title="Blogger blog name">
- <img src="{% href "/content/jquery-openid/images/blogger-1.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/blogger-1.png" %}" alt="icon" />
<span>http://<strong>username</strong>.blogspot.com/</span>
</li>
<li class="username" title="LiveJournal blog name">
- <img src="{% href "/content/jquery-openid/images/livejournal-1.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/livejournal-1.png" %}" alt="icon" />
<span>http://<strong>username</strong>.livejournal.com</span>
</li>
<li class="username" title="ClaimID user name">
- <img src="{% href "/content/jquery-openid/images/claimid-0.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/claimid-0.png" %}" alt="icon" />
<span>http://claimid.com/<strong>username</strong></span>
</li>
<li class="username" title="Vidoop user name">
- <img src="{% href "/content/jquery-openid/images/vidoop.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/vidoop.png" %}" alt="icon" />
<span>http://<strong>username</strong>.myvidoop.com/</span>
</li>
<li class="username" title="Verisign user name">
- <img src="{% href "/content/jquery-openid/images/verisign-2.png" %}" alt="icon" />
+ <img src="{% media "/media/jquery-openid/images/verisign-2.png" %}" alt="icon" />
<span>http://<strong>username</strong>.pip.verisignlabs.com/</span>
</li>
</ul>
diff --git a/templates/authopenid/signup.html b/forum/skins/default/templates/authopenid/signup.html
index d800eaf9..fdb236c2 100644
--- a/templates/authopenid/signup.html
+++ b/forum/skins/default/templates/authopenid/signup.html
@@ -15,10 +15,12 @@
<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="signup_p">{% trans "receive updates motivational blurb" %}</p>
- <p class="signup_p">{% trans "Please select your preferred email update schedule for the following groups of questions:" %}</p>
+ <p class="margin-top">{% trans "receive updates motivational blurb" %}</p>
<div class='simple-subscribe-options'>
{{email_feeds_form.subscribe}}
+ {% if email_feeds_form.errors %}
+ <p class="error">{% trans "please select one of the options above" %}</p>
+ {% endif %}
</div>
<p class="signup_p">{% trans "Please read and type in the two words below to help us prevent automated account creation." %}</p>
{{form.recaptcha}}
diff --git a/templates/authopenid/yadis.xrdf b/forum/skins/default/templates/authopenid/yadis.xrdf
index a9ed44fe..a9ed44fe 100644
--- a/templates/authopenid/yadis.xrdf
+++ b/forum/skins/default/templates/authopenid/yadis.xrdf
diff --git a/templates/badge.html b/forum/skins/default/templates/badge.html
index af6aa2a2..af6aa2a2 100644
--- a/templates/badge.html
+++ b/forum/skins/default/templates/badge.html
diff --git a/templates/badges.html b/forum/skins/default/templates/badges.html
index 8de93df5..8de93df5 100644
--- a/templates/badges.html
+++ b/forum/skins/default/templates/badges.html
diff --git a/templates/base.html b/forum/skins/default/templates/base.html
index 17a32ef2..3a1848ef 100755
--- a/templates/base.html
+++ b/forum/skins/default/templates/base.html
@@ -13,43 +13,31 @@
{% if settings.GOOGLE_SITEMAP_CODE %}
<meta name="google-site-verification" content="{{settings.GOOGLE_SITEMAP_CODE}}" />
{% endif %}
- <link rel="shortcut icon" href="{% href "/content/images/favicon.ico" %}" />
- <link href="{% href "/content/style/style.css" %}" rel="stylesheet" type="text/css" />
+ <link rel="shortcut icon" href="{% media "/media/images/favicon.ico" %}" />
+ <link href="{% media "/media/style/style.css" %}" rel="stylesheet" type="text/css" />
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">google.load("jquery", "1.2.6");</script>
- <script type="text/javascript">
+ <script type="text/javascript">
+ /* <![CDATA[ */
var i18nLang = '{{settings.LANGUAGE_CODE}}';
var scriptUrl = '/{{settings.FORUM_SCRIPT_ALIAS}}'
- </script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.i18n.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.i18n.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.utils.js" %}'></script>
- <!--<script type="text/javascript">
- var uservoiceJsHost = ("https:" == document.location.protocol) ? "https://uservoice.com" : "http://cdn.uservoice.com";
- document.write(unescape("%3Cscript src='" + uservoiceJsHost + "/javascripts/widgets/tab.js' type='text/javascript'%3E%3C/script%3E"))
+ var osqaSkin = '{{settings.OSQA_SKIN}}';
+ /* ]] */
</script>
- <script type="text/javascript">
- UserVoice.Tab.show({
- key: 'cnprog',
- host: 'cnprog.uservoice.com',
- forum: 'general',
- alignment: 'left', /* 'left', 'right' */
- background_color:'#777',
- text_color: 'white', /* 'white', 'black' */
- hover_color: '#06C',
- lang: 'en' /* 'en', 'de', 'nl', 'es', 'fr' */
- })
- </script>-->
- <!-- todo move this to settings -->
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.i18n.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.i18n.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.utils.js" %}'></script>
{% if user_messages %}
<style type="text/css">
body { margin-top:2.4em; }
</style>
<script type="text/javascript">
+ /* <![CDATA[ */
$(document).ready(function() {
$('#validate_email_alert').click(function(){notify.close(true)})
notify.show();
});
+ /* ]] */
</script>
{% endif %}
diff --git a/templates/base_content.html b/forum/skins/default/templates/base_content.html
index eacdc6d0..d1cf673b 100644
--- a/templates/base_content.html
+++ b/forum/skins/default/templates/base_content.html
@@ -9,40 +9,21 @@
{% if settings.GOOGLE_SITEMAP_CODE %}
<meta name="google-site-verification" content="{{ settings.GOOGLE_SITEMAP_CODE }}" />
{% endif %}
- <link rel="shortcut icon" href="{% href "/content/images/favicon.ico" %}" />
- <link href="{% href "/content/style/style.css" %}" rel="stylesheet" type="text/css" />
- {% spaceless %}
+ <link rel="shortcut icon" href="{% media "/media/images/favicon.ico" %}" />
+ <link href="{% media "/media/style/style.css" %}" rel="stylesheet" type="text/css" />
+ {% spaceless %}
{% block forestyle %}{% endblock %}
{% endspaceless %}
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">google.load("jquery", "1.2.6");</script>
- <script type="text/javascript">
+ <script type="text/javascript">
var i18nLang = '{{ settings.LANGUAGE_CODE }}';
var scriptUrl = '/{{settings.FORUM_SCRIPT_ALIAS}}'
- </script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.i18n.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.i18n.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.utils.js" %}'></script>
-
- <!-- <script type="text/javascript">
- var uservoiceJsHost = ("https:" == document.location.protocol) ? "https://uservoice.com" : "http://cdn.uservoice.com";
- document.write(unescape("%3Cscript src='" + uservoiceJsHost + "/javascripts/widgets/tab.js' type='text/javascript'%3E%3C/script%3E"))
+ var osqaSkin = '{{settings.OSQA_SKIN}}';
</script>
- <script type="text/javascript">
- UserVoice.Tab.show({
- //EDIT!!!
- key: 'uservoicekey',
- host: 'where.uservoice.com',
- forum: 'general',
- alignment: 'left', /* 'left', 'right' */
- background_color:'#777',
- text_color: 'white', /* 'white', 'black' */
- hover_color: '#06C',
- lang: 'en' /* 'en', 'de', 'nl', 'es', 'fr' */
- })
- </script>-->
- <!-- todo move this to settings-->
-
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.i18n.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.i18n.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.utils.js" %}'></script>
{% if user_messages %}
<style type="text/css">
body { margin-top:2.4em; }
diff --git a/templates/book.html b/forum/skins/default/templates/book.html
index e83268e4..8574fa73 100644
--- a/templates/book.html
+++ b/forum/skins/default/templates/book.html
@@ -85,12 +85,12 @@
{% if question.favourite_count %}
{% if question.favorited_myself %}
<div class="favorites-count">
- <img title="{% trans "this question was selected as favorite" %} {{question.favourite_count}} {% trans "number of times" %}" src="{% href "/content/images/vote-favorite-on.png" %}">
+ <img title="{% trans "this question was selected as favorite" %} {{question.favourite_count}} {% trans "number of times" %}" src="{% media "/media/images/vote-favorite-on.png" %}">
<div><b>{{question.favourite_count|intcomma}}</b></div>
</div>
{% else %}
<div class="favorites-count-off">
- <img title="{% trans "this question was selected as favorite" %} {{question.favourite_count}} {% trans "number of times" %}" src="{% href "/content/images/vote-favorite-off.png" %}">
+ <img title="{% trans "this question was selected as favorite" %} {{question.favourite_count}} {% trans "number of times" %}" src="{% media "/media/images/vote-favorite-off.png" %}">
<div><b>{{question.favourite_count|intcomma}}</b></div>
</div>
{% endif %}
@@ -122,7 +122,7 @@
</h3>
<div class="tags">
{% for tag in question.tagname_list %}
- <a href="{% url forum.views.tag tag|urlencode %}" title="{% "see questions tagged with" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a>
+ <a href="{% url tag_questions tag|urlencode %}" title="{% "see questions tagged with" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a>
{% endfor %}
</div>
<div class="started">
@@ -144,7 +144,7 @@
</div>
<div class="bookFeed">
<div id="feeds">
- <a href="{% href "/feeds/rss" %} " title="{% trans "subscribe to book RSS feed" %}">{% trans "subscribe to the questions feed" %}</a>
+ <a href="{% media "/feeds/rss" %} " title="{% trans "subscribe to book RSS feed" %}">{% trans "subscribe to the questions feed" %}</a>
</div>
</div>
diff --git a/templates/close.html b/forum/skins/default/templates/close.html
index d9e73507..d9e73507 100644
--- a/templates/close.html
+++ b/forum/skins/default/templates/close.html
diff --git a/templates/edit_user_email_feeds_form.html b/forum/skins/default/templates/edit_user_email_feeds_form.html
index 65902e7e..65902e7e 100644
--- a/templates/edit_user_email_feeds_form.html
+++ b/forum/skins/default/templates/edit_user_email_feeds_form.html
diff --git a/templates/faq.html b/forum/skins/default/templates/faq.html
index 236f4f76..236f4f76 100644
--- a/templates/faq.html
+++ b/forum/skins/default/templates/faq.html
diff --git a/forum/skins/default/templates/fbconnect/xd_receiver.html b/forum/skins/default/templates/fbconnect/xd_receiver.html
new file mode 100755
index 00000000..a03c61bc
--- /dev/null
+++ b/forum/skins/default/templates/fbconnect/xd_receiver.html
@@ -0,0 +1,10 @@
+<!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" >
+{% load i18n %}
+ <head>
+ <title>{% blocktrans %}Connect to {{APP_SHORT_NAME}} with Facebook!{% endblocktrans %}
+ </head>
+ <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/templates/feedback.html b/forum/skins/default/templates/feedback.html
index 38bb48ff..38bb48ff 100644
--- a/templates/feedback.html
+++ b/forum/skins/default/templates/feedback.html
diff --git a/templates/feedback_email.txt b/forum/skins/default/templates/feedback_email.txt
index df768180..df768180 100644
--- a/templates/feedback_email.txt
+++ b/forum/skins/default/templates/feedback_email.txt
diff --git a/templates/feeds/rss_description.html b/forum/skins/default/templates/feeds/rss_description.html
index fa781907..fa781907 100644
--- a/templates/feeds/rss_description.html
+++ b/forum/skins/default/templates/feeds/rss_description.html
diff --git a/templates/feeds/rss_title.html b/forum/skins/default/templates/feeds/rss_title.html
index 7899fce3..7899fce3 100644
--- a/templates/feeds/rss_title.html
+++ b/forum/skins/default/templates/feeds/rss_title.html
diff --git a/templates/footer.html b/forum/skins/default/templates/footer.html
index 66feff8a..89d4801f 100644
--- a/templates/footer.html
+++ b/forum/skins/default/templates/footer.html
@@ -28,7 +28,7 @@
</div>
<div id="licenseLogo">
<a href="http://creativecommons.org/licenses/by/3.0/">
- <img src="{% href "/content/images/cc-wiki.png" %}" title="Creative Commons: Attribution - Share Alike" alt="cc-wiki" width="50" height="68" />
+ <img src="{% media "/media/images/cc-wiki.png" %}" title="Creative Commons: Attribution - Share Alike" alt="cc-wiki" width="50" height="68" />
</a>
</div>
</div>
diff --git a/templates/header.html b/forum/skins/default/templates/header.html
index 466659de..3afc46c5 100644
--- a/templates/header.html
+++ b/forum/skins/default/templates/header.html
@@ -18,7 +18,7 @@
<td width="23%">
<div id="logo">
<a href="{% url index %}">
- <img src="{% href "/content/images/logo.png" %}" title="{% trans "back to home page" %}" alt="{{settings.APP_TITLE}} logo"/>
+ <img src="{% media "/media/images/logo.png" %}" title="{% trans "back to home page" %}" alt="{{settings.APP_TITLE}} logo"/>
</a>
</div>
</td>
diff --git a/forum/skins/default/templates/index.html b/forum/skins/default/templates/index.html
new file mode 100755
index 00000000..5bbb192b
--- /dev/null
+++ b/forum/skins/default/templates/index.html
@@ -0,0 +1,124 @@
+{% extends "base.html" %}
+<!-- index.html -->
+{% load i18n %}
+{% load extra_tags %}
+{% load humanize %}
+{% load extra_filters %}
+{% load smart_if %}
+{% block title %}{% spaceless %}{% trans "Home" %}{% endspaceless %}{% endblock %}
+{% block meta %}<meta name="keywords" content="{{ settings.APP_KEYWORDS }}" />
+ <meta name="description" content="{{ settings.APP_DESCRIPTION }}" />{% endblock %}
+{% block forejs %}
+ <script type="text/javascript">
+ var tags = {{ tags_autocomplete|safe }};
+ $().ready(function(){
+ var tab_id = "{{ tab_id }}";
+ $("#"+tab_id).attr('className',"on");
+ $("#nav_questions").attr('className',"on");
+ });
+ </script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.tag_selector.js" %}'></script>
+{% endblock %}
+{% block content %}
+<div class="tabBar">
+ <div class="headQuestions">{% trans "Questions" %}</div>
+ <div class="tabsA">
+ <a id="latest" href="{% url questions %}?sort=latest" title="{% trans "last updated questions" %}" >{% trans "newest" %}</a>
+ <a id="hottest" href="{% url questions %}?sort=hottest" title="{% trans "hottest questions" %}" >{% trans "hottest" %}</a>
+ <a id="mostvoted" href="{% url questions %}?sort=mostvoted" title="{% trans "most voted questions" %}" >{% trans "most voted" %}</a>
+ <a id="all" href="{% url questions %}" title="{% trans "all questions" %}" >{% trans "all questions" %}</a>
+ </div>
+</div>
+<!-- ???? -->
+<div id="listA">
+ {% for question in questions.object_list %}
+ <div class="short-summary">
+ <div class="counts">
+ <div class="votes">
+ <div class="item-count">{{question.score|intcomma}}</div>
+ <div>{% trans "votes" %}</div>
+ </div >
+ <div {% if question.answer_accepted %}title="{% trans "this answer has been accepted to be correct" %}"{% endif %} class="status {% if question.answer_accepted %}answered-accepted{% endif %} {% ifequal question.answer_count 0 %}unanswered{% endifequal %}{% ifnotequal question.answer_count 0 %}answered{% endifnotequal %}">
+ <div class="item-count">{{question.answer_count|intcomma}}</div>
+ <div>{% trans "answers" %}</div>
+ </div>
+ <div class="views">
+ <div class="item-count">{{question.view_count|cnprog_intword|safe}}</div>
+ <div>{% trans "views" %}</div>
+ </div>
+ </div>
+
+ <h2><a title="{{question.summary}}" href="{% url question id=question.id %}{{question.title|slugify}}">{{question.title}}</a></h2>
+
+ <div class="userinfo">
+ <span class="relativetime" title="{{question.last_activity_at}}">{% diff_date question.last_activity_at %}</span>
+ {% if question.last_activity_by %}
+ <a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a> {% get_score_badge question.last_activity_by %}
+ {% endif %}
+ </div>
+
+ <div class="tags">
+ {% for tag in question.tagname_list %}
+ <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %} '{{ tag }}' {% trans "using tags" %}" rel="tag">{{ tag }}</a>
+ {% endfor %}
+ </div>
+ </div>
+ {% endfor %}
+</div>
+{% endblock %}
+
+{% block sidebar %}
+{% if not request.user.is_authenticated %}
+<div class="boxA">
+ <h3>{% trans "welcome to website" %}</h3>
+ <div class="body">
+ {{ settings.APP_INTRO|safe }}
+ <div class="more"><a href="{% url about %}">{% trans "about" %} </a></div>
+ <div class="more"><a href="{% url faq %}">{% trans "faq" %} </a></div>
+ </div>
+</div>
+{% else %}
+{% include "tag_selector.html" %}
+{% endif %}
+<div class="boxC">
+ <h3>{% trans "Recent tags" %}</h3>
+ <div class="body">
+ <div class="tags">
+ {% for tag in tags %}
+ <a rel="tag"
+ title="{% blocktrans with tag.name as tagname %}see questions tagged '{{tagname}}'{% endblocktrans %}" href="{% url tag_questions tag.name|urlencode %}">{{ tag.name }}</a>
+ {% endfor %}
+ </div>
+ <div class="more"><a href="{% url tags %}">{% trans "popular tags" %} </a> </div>
+ </div>
+</div>
+{% if awards %}
+<div class="boxC">
+ <h3>{% trans "Recent awards" %}</h3>
+ <div class="body">
+ <ul class="badge-list">
+ {% for award in awards %}
+ <li>
+ <a href="{% url badges %}{{award.badge_id}}/{{award.badge_name}}" title="{{ award.badge_description }}" class="medal">
+ <span class="badge{{ award.badge_type }}">&#9679;</span>&nbsp;{{ award.badge_name }}</a> {% trans "given to" %}
+ <a href="{% url users %}{{award.user_id}}/{{award.user_name}}">{{ award.user_name }}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ <div class="more"><a href="{% url badges %}">{% trans "all awards" %} </a> </div>
+ </div>
+</div>
+{% endif %}
+<div id="feeds">
+<a href="{% media "/feeds/rss" %}" title="{% trans "subscribe to last 30 questions by RSS" %}">{% trans "subscribe to the questions feed" %}</a>
+</div>
+{% endblock %}
+{% block tail %}
+<div class="pager">{% cnprog_paginator context %}</div>
+ <div class="pagesize">{% cnprog_pagesize context %}</div>
+<!-- <div style="padding:5px 0 5px 5px;">
+<span class="evenMore">{% trans "Still looking for more? See" %} <a href="{% url questions %}">{% trans "complete list of questions" %}</a> {% trans "or" %} <a href="{% url tags %}">{% trans "popular tags" %}</a>{% trans "." %} {% trans "Please help us answer" %} <a href="{% url questions %}unanswered">{% trans "list of unanswered questions" %}</a>{% trans "." %}</span>
+</div> -->
+{% endblock %}
+<!-- index.html --> \ No newline at end of file
diff --git a/forum/skins/default/templates/index_.html b/forum/skins/default/templates/index_.html
new file mode 100755
index 00000000..5e4cf533
--- /dev/null
+++ b/forum/skins/default/templates/index_.html
@@ -0,0 +1,124 @@
+{% extends "base.html" %}
+<!-- index.html -->
+{% load i18n %}
+{% load extra_tags %}
+{% load humanize %}
+{% load extra_filters %}
+{% load smart_if %}
+{% block title %}{% spaceless %}{% trans "Home" %}{% endspaceless %}{% endblock %}
+{% block meta %}<meta name="keywords" content="{{ settings.APP_KEYWORDS }}" />
+ <meta name="description" content="{{ settings.APP_DESCRIPTION }}" />{% endblock %}
+{% block forejs %}
+ <script type="text/javascript">
+ var tags = {{ tags_autocomplete|safe }};
+ $().ready(function(){
+ var tab_id = "{{ tab_id }}";
+ $("#"+tab_id).attr('className',"on");
+ $("#nav_questions").attr('className',"on");
+ });
+ </script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.tag_selector.js" %}'></script>
+{% endblock %}
+{% block content %}
+<div class="tabBar">
+ <div class="headQuestions">{% trans "Questions" %}</div>
+ <div class="tabsA">
+ <a id="latest" href="{% url index %}?sort=latest" title="{% trans "last updated questions" %}" >{% trans "newest" %}</a>
+ <a id="hottest" href="{% url index %}?sort=hottest" title="{% trans "hottest questions" %}" >{% trans "hottest" %}</a>
+ <a id="mostvoted" href="{% url index %}?sort=mostvoted" title="{% trans "most voted questions" %}" >{% trans "most voted" %}</a>
+ <a id="all" href="{% url index %}" title="{% trans "all questions" %}" >{% trans "all questions" %}</a>
+ </div>
+</div>
+
+<div id="listA">
+ {% for question in questions.object_list %}
+ <div class="short-summary">
+ <div class="counts">
+ <div class="votes">
+ <div class="item-count">{{question.score|intcomma}}</div>
+ <div>{% trans "votes" %}</div>
+ </div >
+ <div {% if question.answer_accepted %}title="{% trans "this answer has been accepted to be correct" %}"{% endif %} class="status {% if question.answer_accepted %}answered-accepted{% endif %} {% ifequal question.answer_count 0 %}unanswered{% endifequal %}{% ifnotequal question.answer_count 0 %}answered{% endifnotequal %}">
+ <div class="item-count">{{question.answer_count|intcomma}}</div>
+ <div>{% trans "answers" %}</div>
+ </div>
+ <div class="views">
+ <div class="item-count">{{question.view_count|cnprog_intword|safe}}</div>
+ <div>{% trans "views" %}</div>
+ </div>
+ </div>
+
+ <h2><a title="{{question.summary}}" href="{% url question id=question.id %}{{question.title|slugify}}">{{question.title}}</a></h2>
+
+ <div class="userinfo">
+ <span class="relativetime" title="{{question.last_activity_at}}">{% diff_date question.last_activity_at %}</span>
+ {% if question.last_activity_by %}
+ <a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a> {% get_score_badge question.last_activity_by %}
+ {% endif %}
+ </div>
+
+ <div class="tags">
+ {% for tag in question.tagname_list %}
+ <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %} '{{ tag }}' {% trans "using tags" %}" rel="tag">{{ tag }}</a>
+ {% endfor %}
+ </div>
+ </div>
+ {% endfor %}
+</div>
+{% endblock %}
+
+{% block sidebar %}
+{% if not request.user.is_authenticated %}
+<div class="boxA">
+ <h3>{% trans "welcome to website" %}</h3>
+ <div class="body">
+ {{ settings.APP_INTRO|safe }}
+ <div class="more"><a href="{% url about %}">{% trans "about" %} »</a></div>
+ <div class="more"><a href="{% url faq %}">{% trans "faq" %} »</a></div>
+ </div>
+</div>
+{% else %}
+{% include "tag_selector.html" %}
+{% endif %}
+<div class="boxC">
+ <h3>{% trans "Recent tags" %}</h3>
+ <div class="body">
+ <div class="tags">
+ {% for tag in tags %}
+ <a rel="tag"
+ title="{% blocktrans with tag.name as tagname %}see questions tagged '{{tagname}}'{% endblocktrans %}" href="{% url tag_questions tag.name|urlencode %}">{{ tag.name }}</a>
+ {% endfor %}
+ </div>
+ <div class="more"><a href="{% url tags %}">{% trans "popular tags" %} »</a> </div>
+ </div>
+</div>
+{% if awards %}
+<div class="boxC">
+ <h3>{% trans "Recent awards" %}</h3>
+ <div class="body">
+ <ul class="badge-list">
+ {% for award in awards %}
+ <li>
+ <a href="{% url badges %}{{award.badge_id}}/{{award.badge_name}}" title="{{ award.badge_description }}" class="medal">
+ <span class="badge{{ award.badge_type }}">&#9679;</span>&nbsp;{{ award.badge_name }}</a>
+ <a href="{% url users %}{{award.user_id}}/{{award.user_name}}">{{ award.user_name }}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ <div class="more"><a href="{% url badges %}">{% trans "all awards" %} »</a> </div>
+ </div>
+</div>
+{% endif %}
+<div id="feeds">
+<a href="{% media "/feeds/rss" %}" title="{% trans "subscribe to last 30 questions by RSS" %}">{% trans "subscribe to the questions feed" %}</a>
+</div>
+{% endblock %}
+{% block tail %}
+<div class="pager">{% cnprog_paginator context %}</div>
+ <div class="pagesize">{% cnprog_pagesize context %}</div>
+<!-- <div style="padding:5px 0 5px 5px;">
+<span class="evenMore">{% trans "Still looking for more? See" %} <a href="{% url questions %}">{% trans "complete list of questions" %}</a> {% trans "or" %} <a href="{% url tags %}">{% trans "popular tags" %}</a>{% trans "." %} {% trans "Please help us answer" %} <a href="{% url questions %}unanswered">{% trans "list of unanswered questions" %}</a>{% trans "." %}</span>
+</div> -->
+{% endblock %}
+<!-- index.html -->
diff --git a/templates/logout.html b/forum/skins/default/templates/logout.html
index 650ba044..650ba044 100644
--- a/templates/logout.html
+++ b/forum/skins/default/templates/logout.html
diff --git a/templates/notarobot.html b/forum/skins/default/templates/notarobot.html
index 698c5696..698c5696 100644
--- a/templates/notarobot.html
+++ b/forum/skins/default/templates/notarobot.html
diff --git a/templates/pagesize.html b/forum/skins/default/templates/pagesize.html
index 5037f1f6..5037f1f6 100644
--- a/templates/pagesize.html
+++ b/forum/skins/default/templates/pagesize.html
diff --git a/templates/paginator.html b/forum/skins/default/templates/paginator.html
index 2fba5425..2fba5425 100644
--- a/templates/paginator.html
+++ b/forum/skins/default/templates/paginator.html
diff --git a/templates/post_contributor_info.html b/forum/skins/default/templates/post_contributor_info.html
index 9997be5f..9997be5f 100644
--- a/templates/post_contributor_info.html
+++ b/forum/skins/default/templates/post_contributor_info.html
diff --git a/templates/privacy.html b/forum/skins/default/templates/privacy.html
index e66086dd..e66086dd 100644
--- a/templates/privacy.html
+++ b/forum/skins/default/templates/privacy.html
diff --git a/templates/question.html b/forum/skins/default/templates/question.html
index 3955c059..fe9f5cde 100644
--- a/templates/question.html
+++ b/forum/skins/default/templates/question.html
@@ -11,13 +11,13 @@
<meta name="keywords" content="{{question.tagname_meta_generator}}" />
<link rel="canonical" href="{{settings.APP_URL}}{{question.get_absolute_url}}" />
{% if not question.closed %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/wmd/showdown.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/wmd/wmd.js" %}'></script>
- <link rel="stylesheet" type="text/css" href="{% href "/content/js/wmd/wmd.css" %}" />
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/wmd/showdown.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/wmd/wmd.js" %}'></script>
+ <link rel="stylesheet" type="text/css" href="{% media "/media/js/wmd/wmd.css" %}" />
{% endif %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.validate.pack.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.post.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.validate.pack.js" %}'></script>
<script type="text/javascript">
// define reputation needs for comments
@@ -70,55 +70,53 @@
{% if question_vote %}
<img id="question-img-upvote-{{ question.id }}" class="question-img-upvote"
{% if question_vote.is_upvote %}
- src="{% href "/content/images/vote-arrow-up-on.png" %}"
+ src="{% media "/media/images/vote-arrow-up-on.png" %}"
{% else %}
- src="{% href "/content/images/vote-arrow-up.png" %}"
+ src="{% media "/media/images/vote-arrow-up.png" %}"
{% endif %}
alt="{% trans "i like this post (click again to cancel)" %}"
- title="{% trans "i like this post (click again to cancel)" %}" />
+ title="{% trans "i like this post (click again to cancel)" %}" />
<div id="question-vote-number-{{ question.id }}" class="vote-number"
- title="{% trans "current number of votes" %}">
+ title="{% trans "current number of votes" %}">
{{ question.score }}
</div>
<img id="question-img-downvote-{{ question.id }}" class="question-img-downvote"
{% if question_vote.is_downvote %}
- src="{% href "/content/images/vote-arrow-down-on.png" %}"
+ src="{% media "/media/images/vote-arrow-down-on.png" %}"
{% else %}
- src="{% href "/content/images/vote-arrow-down.png" %}"
+ src="{% media "/media/images/vote-arrow-down.png" %}"
{% endif %}
alt="{% trans "i dont like this post (click again to cancel)" %}"
- title="{% trans "i dont like this post (click again to cancel)" %}" />
+ title="{% trans "i dont like this post (click again to cancel)" %}" />
{% else %}
<img id="question-img-upvote-{{ question.id }}" class="question-img-upvote"
alt="{% trans "i like this post (click again to cancel)" %}"
- src="{% href "/content/images/vote-arrow-up.png" %}"
- title="{% trans "i like this post (click again to cancel)" %}" />
+ src="{% media "/media/images/vote-arrow-up.png" %}"
+ title="{% trans "i like this post (click again to cancel)" %}" />
<div id="question-vote-number-{{ question.id }}" class="vote-number"
- title="{% trans "current number of votes" %}">
+ title="{% trans "current number of votes" %}">
{{ question.score }}
</div>
<img id="question-img-downvote-{{ question.id }}" class="question-img-downvote"
- src="{% href "/content/images/vote-arrow-down.png" %}"
+ src="{% media "/media/images/vote-arrow-down.png" %}"
alt="{% trans "i dont like this post (click again to cancel)" %}"
title="{% trans "i dont like this post (click again to cancel)" %}" />
-
{% endif %}
{% if favorited %}
- <img class="question-img-favorite" src="{% href "/content/images/vote-favorite-on.png" %}"
- alt="{% trans "mark this question as favorite (click again to cancel)" %}"
- title="{% trans "mark this question as favorite (click again to cancel)" %}" />
+ <img class="question-img-favorite" src="{% media "/media/images/vote-favorite-on.png" %}"
+ alt="{% trans "mark this question as favorite (click again to cancel)" %}"
+ title="{% trans "mark this question as favorite (click again to cancel)" %}" />
<div id="favorite-number" class="favorite-number my-favorite-number">
{{ question.favourite_count }}
</div>
{% else %}
- <img class="question-img-favorite" src="{% href "/content/images/vote-favorite-off.png" %}"
- alt="{% trans "remove favorite mark from this question (click again to restore mark)" %}"
- title="{% trans "remove favorite mark from this question (click again to restore mark)" %}" />
+ <img class="question-img-favorite" src="{% media "/media/images/vote-favorite-off.png" %}"
+ alt="{% trans "remove favorite mark from this question (click again to restore mark)" %}"
+ title="{% trans "remove favorite mark from this question (click again to restore mark)" %}" />
<div id="favorite-number" class="favorite-number">
{% ifnotequal question.favourite_count 0 %}{{ question.favourite_count }}{% endifnotequal %}
</div>
-
{% endif %}
</div>
@@ -131,7 +129,7 @@
<div id="question-controls" class="post-controls">
<div id="question-tags" class="tags">
{% for tag in question.tagname_list %}
- <a href="{% url forum.views.tag tag|urlencode %}" class="post-tag"
+ <a href="{% url tag_questions tag|urlencode %}" class="post-tag"
title="{% blocktrans with tag as tagname %}see questions tagged '{{ tagname }}'{% endblocktrans %}" rel="tag">{{ tag }}</a>
{% endfor %}
</div>
@@ -178,7 +176,7 @@
<span class="comment-age">({% diff_date comment.added_at %})</span>
{% if request.user|can_delete_comment:comment %}
<img class="delete-icon"
- src="{% href "/content/images/close-small.png" %}"
+ src="{% media "/media/images/close-small.png" %}"
title="{% trans "delete this comment" %}"/>
{% endif %}
{% endspaceless %}
@@ -217,8 +215,8 @@
{% if question.closed %}
<div class="question-status" style="margin-bottom:15px">
<h3>{% blocktrans with question.get_close_reason_display as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %}
- <a href="{{ question.closed_by.get_profile_url }}">{{ question.closed_by.username }}</a>
- {% blocktrans with question.closed_at as closed_at %}close date {{closed_at}}{% endblocktrans %}</h3>
+ <a href="{{ question.closed_by.get_profile_url }}">{{ question.closed_by.username }}</a>
+ {% blocktrans with question.closed_at as closed_at %}close date {{closed_at}}{% endblocktrans %}</h3>
</div>
{% endif %}
{% if answers %}
@@ -251,28 +249,28 @@
<td style="width:30px;vertical-align:top">
<div class="vote-buttons">
<img id="answer-img-upvote-{{ answer.id }}" class="answer-img-upvote"
- src="{% blockresource %}/content/images/vote-arrow-up{% get_user_vote_image user_answer_votes answer.id 1 %}.png{% endblockresource %}"
+ src="{% blockmedia %}/media/images/vote-arrow-up{% get_user_vote_image user_answer_votes answer.id 1 %}.png{% endblockmedia %}"
alt="{% trans "i like this answer (click again to cancel)" %}"
title="{% trans "i like this answer (click again to cancel)" %}"/>
<div id="answer-vote-number-{{ answer.id }}" class="vote-number" title="{% trans "current number of votes" %}">
{{ answer.score }}
</div>
<img id="answer-img-downvote-{{ answer.id }}" class="answer-img-downvote"
- src="{% blockresource %}/content/images/vote-arrow-down{% get_user_vote_image user_answer_votes answer.id -1 %}.png{% endblockresource %}"
- alt="{% trans "i dont like this answer (click again to cancel)" %}"
- title="{% trans "i dont like this answer (click again to cancel)" %}" />
+ src="{% blockmedia %}/media/images/vote-arrow-down{% get_user_vote_image user_answer_votes answer.id -1 %}.png{% endblockmedia %}"
+ alt="{% trans "i dont like this answer (click again to cancel)" %}"
+ title="{% trans "i dont like this answer (click again to cancel)" %}" />
{% ifequal request.user question.author %}
<img id="answer-img-accept-{{ answer.id }}" class="answer-img-accept"
- src="{% blockresource %}/content/images/vote-accepted{% if answer.accepted %}-on{% endif %}.png{% endblockresource %}"
- alt="{% trans "mark this answer as favorite (click again to undo)" %}"
- title="{% trans "mark this answer as favorite (click again to undo)" %}" />
+ src="{% blockmedia %}/media/images/vote-accepted{% if answer.accepted %}-on{% endif %}.png{% endblockmedia %}"
+ alt="{% trans "mark this answer as favorite (click again to undo)" %}"
+ title="{% trans "mark this answer as favorite (click again to undo)" %}" />
{% else %}
{% if answer.accepted %}
<img id="answer-img-accept-{{ answer.id }}" class="answer-img-accept"
- src="{% blockresource %}/content/images/vote-accepted{% if answer.accepted %}-on{% endif %}.png{% endblockresource %}"
- alt="{% trans "the author of the question has selected this answer as correct" %}"
- title="{% trans "the author of the question has selected this answer as correct" %}" />
+ src="{% blockmedia %}/media/images/vote-accepted{% if answer.accepted %}-on{% endif %}.png{% endblockmedia %}"
+ alt="{% trans "the author of the question has selected this answer as correct" %}"
+ title="{% trans "the author of the question has selected this answer as correct" %}" />
{% endif %}
{% endifequal %}
</div>
@@ -327,7 +325,7 @@
<span class="comment-age">({% diff_date comment.added_at %})</span>
{% if request.user|can_delete_comment:comment %}
<img class="delete-icon"
- src="{% href "/content/images/close-small.png" %}"
+ src="{% media "/media/images/close-small.png" %}"
title="{% trans "delete this comment" %}"/>
{% endif %}
{% endspaceless %}
@@ -476,7 +474,7 @@
</p>
<p class="tags" >
{% for tag in tags %}
- <a href="{% url forum.views.tag tag.name|urlencode %}"
+ <a href="{% url tag_questions tag.name|urlencode %}"
title="{% trans "see questions tagged"%}'{{tag.name}}'{% trans "using tags" %}"
rel="tag">{{ tag.name }}</a> <span class="tag-number">&#215;{{ tag.used_count|intcomma }}</span><br/>
{% endfor %}
diff --git a/templates/question_edit.html b/forum/skins/default/templates/question_edit.html
index 8ce980fe..fe711849 100644
--- a/templates/question_edit.html
+++ b/forum/skins/default/templates/question_edit.html
@@ -4,12 +4,12 @@
{% load extra_tags %}
{% block title %}{% spaceless %}{% trans "Edit question" %}{% endspaceless %}{% endblock %}
{% block forejs %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.validate.pack.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/wmd/showdown.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/wmd/wmd.js" %}'></script>
- <link rel="stylesheet" type="text/css" href="{% href "/content/js/wmd/wmd.css" %}" />
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.post.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.validate.pack.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/wmd/showdown.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/wmd/wmd.js" %}'></script>
+ <link rel="stylesheet" type="text/css" href="{% media "/media/js/wmd/wmd.css" %}" />
<script type="text/javascript">
//todo move javascript out
$().ready(function(){
diff --git a/templates/question_edit_tips.html b/forum/skins/default/templates/question_edit_tips.html
index 4cabea79..4cabea79 100644
--- a/templates/question_edit_tips.html
+++ b/forum/skins/default/templates/question_edit_tips.html
diff --git a/templates/question_retag.html b/forum/skins/default/templates/question_retag.html
index b7957962..03f3da04 100644
--- a/templates/question_retag.html
+++ b/forum/skins/default/templates/question_retag.html
@@ -3,9 +3,9 @@
{% load extra_tags %}
{% block title %}{% spaceless %}{% trans "Change tags" %}{% endspaceless %}{% endblock %}
{% block forejs %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.validate.pack.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.post.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.validate.pack.js" %}'></script>
<script type="text/javascript">
$().ready(function(){
diff --git a/templates/question_summary_list_roll.html b/forum/skins/default/templates/question_summary_list_roll.html
index 7312dca9..57685d6d 100644
--- a/templates/question_summary_list_roll.html
+++ b/forum/skins/default/templates/question_summary_list_roll.html
@@ -49,7 +49,7 @@
<div class="tags">
{% for tag in question.tagname_list %}
- <a href="{% url forum.views.tag tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a>
+ <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a>
{% endfor %}
</div>
</div>
diff --git a/templates/questions.html b/forum/skins/default/templates/questions.html
index 67751996..4c3b96d2 100644
--- a/templates/questions.html
+++ b/forum/skins/default/templates/questions.html
@@ -19,8 +19,8 @@
Hilite.debug_referrer = location.href;
});
</script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.tag_selector.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.tag_selector.js" %}'></script>
{% endblock %}
{% block content %}
<div class="tabBar">
@@ -133,7 +133,7 @@
<div class="tags">
{% for tag in question.tagname_list %}
- <a href="{% url forum.views.tag tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a>
+ <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a>
{% endfor %}
</div>
</div>
@@ -224,7 +224,7 @@
<h3 class="subtitle">{% trans "Related tags" %}</h3>
<div class="tags">
{% for tag in tags %}
- <a rel="tag" title="{% blocktrans with tag.name as tag_name %}see questions tagged '{{ tag_name }}'{% endblocktrans %}" href="{% url forum.views.tag tag.name|urlencode %}">{{ tag.name }}</a>
+ <a rel="tag" title="{% blocktrans with tag.name as tag_name %}see questions tagged '{{ tag_name }}'{% endblocktrans %}" href="{% url tag_questions tag.name|urlencode %}">{{ tag.name }}</a>
<span class="tag-number">&#215; {{ tag.used_count|intcomma }}</span>
<br />
{% endfor %}
diff --git a/templates/reopen.html b/forum/skins/default/templates/reopen.html
index 37fb69c1..37fb69c1 100644
--- a/templates/reopen.html
+++ b/forum/skins/default/templates/reopen.html
diff --git a/templates/revisions_answer.html b/forum/skins/default/templates/revisions_answer.html
index 974e589c..b2e33dfe 100644
--- a/templates/revisions_answer.html
+++ b/forum/skins/default/templates/revisions_answer.html
@@ -6,8 +6,8 @@
{% load humanize %}
{% block title %}{% spaceless %}{% trans "Revision history" %}{% endspaceless %}{% endblock %}
{% block forejs %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.post.js" %}'></script>
<script type="text/javascript">
//todo - take this out into .js file
$().ready(function(){
@@ -24,7 +24,7 @@
var arrow = $("#rev-arrow-" + id);
var visible = arrow.attr("src").indexOf("hide") > -1;
- var path = $.i18n._('/') + "content/images/expander-arrow-" +
+ var path = $.i18n._('/') + "media/images/expander-arrow-" +
(visible ? "show" : "hide") + ".gif" + "?v={{settings.RESOURCE_REVISION}}";
arrow.attr("src", path);
$("#rev-body-" + id).slideToggle("fast");
@@ -46,7 +46,7 @@
<table width="100%">
<tr>
<td width="20" style="vertical-align:middle"><img id="rev-arrow-{{ revision.revision }}"
- src="{% href "/content/images/expander-arrow-show.gif" %}"
+ src="{% media "/media/images/expander-arrow-show.gif" %}"
alt="{% trans "click to hide/show revision" %}"/>
</td>
<td width="30px" style="vertical-align:middle"><span class="revision-number" title="{% trans "revision" %} {{ revision.revision }}">{{ revision.revision }}</span></td>
diff --git a/templates/revisions_question.html b/forum/skins/default/templates/revisions_question.html
index 83512e4a..86d52a36 100644
--- a/templates/revisions_question.html
+++ b/forum/skins/default/templates/revisions_question.html
@@ -7,8 +7,8 @@
{% load humanize %}
{% block title %}{% spaceless %}{% trans "Revision history" %}{% endspaceless %}{% endblock %}
{% block forejs %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.post.js" %}'></script>
<script type="text/javascript">
//todo - take this out into .js file
$().ready(function(){
@@ -25,7 +25,7 @@
var arrow = $("#rev-arrow-" + id);
var visible = arrow.attr("src").indexOf("hide") > -1;
- var path = $.i18n._('/') + "content/images/expander-arrow-" +
+ var path = $.i18n._('/') + "media/images/expander-arrow-" +
(visible ? "show" : "hide") + ".gif" + "?v={{settings.RESOURCE_REVISION}}";
arrow.attr("src", path);
$("#rev-body-" + id).slideToggle("fast");
@@ -46,7 +46,7 @@
<table width="100%">
<tr>
<td width="20" style="vertical-align:middle"><img id="rev-arrow-{{ revision.revision }}"
- src="{% href "/content/images/expander-arrow-show.gif" %}"
+ src="{% media "/media/images/expander-arrow-show.gif" %}"
alt="{% trans "click to hide/show revision" %}"/>
</td>
<td width="30px" style="vertical-align:middle"><span class="revision-number" title="{% trans "revision" %} {{ revision.revision }}">{{ revision.revision }}</span></td>
diff --git a/templates/tag_selector.html b/forum/skins/default/templates/tag_selector.html
index 94d23f3c..7686d717 100644
--- a/templates/tag_selector.html
+++ b/forum/skins/default/templates/tag_selector.html
@@ -10,7 +10,7 @@
title="{% blocktrans with tag as tagname %}see questions tagged '{{ tag_name }}'{% endblocktrans %}"
href="{% url tag_questions tag_name|urlencode %}">{{tag_name}}</a>
<img class="delete-icon"
- src="{% href "/content/images/close-small-dark.png" %}"
+ src="{% media "/media/images/close-small-dark.png" %}"
title="{% blocktrans %}remove '{{tag_name}}' from the list of interesting tags{% endblocktrans %}"/>
</span>
{% endspaceless %}
@@ -27,7 +27,7 @@
title="{% blocktrans with tag as tagname %}see questions tagged '{{ tag_name }}'{% endblocktrans %}"
href="{% url tag_questions tag_name|urlencode %}">{{tag_name}}</a>
<img class="delete-icon"
- src="{% href "/content/images/close-small-dark.png" %}"
+ src="{% media "/media/images/close-small-dark.png" %}"
title="{% blocktrans %}remove '{{tag_name}}' from the list of ignored tags{% endblocktrans %}"/>
</span>
{% endspaceless %}
diff --git a/templates/tags.html b/forum/skins/default/templates/tags.html
index 1bde187f..50f90fb1 100644
--- a/templates/tags.html
+++ b/forum/skins/default/templates/tags.html
@@ -46,7 +46,7 @@
<ul class="tagsList tags">
{% for tag in tags.object_list %}
<li>
- <a href="{% url forum.views.tag tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">
+ <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">
{{ tag }}
</a>&nbsp;
<span class="tag-number">&#215; {{ tag.used_count|intcomma }}</span>
diff --git a/templates/user.html b/forum/skins/default/templates/user.html
index 6e4098e9..5931f31c 100644
--- a/templates/user.html
+++ b/forum/skins/default/templates/user.html
@@ -12,8 +12,8 @@
{% endblock %}
{% block forejs %}
{% if request.user|can_moderate_users %}
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.admin.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.form.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/com.cnprog.admin.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.form.js" %}'></script>
{% endif %}
<script type="text/javascript">
var viewUserID = {{view_user.id}};
diff --git a/templates/user_edit.html b/forum/skins/default/templates/user_edit.html
index bc5056f9..040ebff4 100644
--- a/templates/user_edit.html
+++ b/forum/skins/default/templates/user_edit.html
@@ -24,7 +24,7 @@
{% if request.user.email %}
{% gravatar request.user 128 %}
{% else %}
- <img src="{% href "/content/images/nophoto.png" %}">
+ <img src="{% media "/media/images/nophoto.png" %}">
{% endif %}
<div style="padding:20px 0 0 20px;font-weight:bold;font-size:150%">
<a href="http://www.gravatar.com/" target="_blank"
diff --git a/templates/user_email_subscriptions.html b/forum/skins/default/templates/user_email_subscriptions.html
index c0204cbc..c0204cbc 100644
--- a/templates/user_email_subscriptions.html
+++ b/forum/skins/default/templates/user_email_subscriptions.html
diff --git a/templates/user_favorites.html b/forum/skins/default/templates/user_favorites.html
index 9db01e9a..9db01e9a 100644
--- a/templates/user_favorites.html
+++ b/forum/skins/default/templates/user_favorites.html
diff --git a/templates/user_footer.html b/forum/skins/default/templates/user_footer.html
index ee347742..ee347742 100644
--- a/templates/user_footer.html
+++ b/forum/skins/default/templates/user_footer.html
diff --git a/templates/user_info.html b/forum/skins/default/templates/user_info.html
index c550e13f..c550e13f 100644
--- a/templates/user_info.html
+++ b/forum/skins/default/templates/user_info.html
diff --git a/templates/user_recent.html b/forum/skins/default/templates/user_recent.html
index b704ab25..b704ab25 100644
--- a/templates/user_recent.html
+++ b/forum/skins/default/templates/user_recent.html
diff --git a/templates/user_reputation.html b/forum/skins/default/templates/user_reputation.html
index 16127140..776935ae 100644
--- a/templates/user_reputation.html
+++ b/forum/skins/default/templates/user_reputation.html
@@ -4,8 +4,8 @@
{% load extra_filters %}
{% load humanize %}
{% block userjs %}
- <script type='text/javascript' src='{% href "/content/js/excanvas.pack.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/jquery.flot.pack.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/excanvas.pack.js" %}'></script>
+ <script type='text/javascript' src='{% media "/media/js/jquery.flot.pack.js" %}'></script>
<script type="text/javascript">
$().ready(function(){
diff --git a/templates/user_responses.html b/forum/skins/default/templates/user_responses.html
index c4f4ffed..c4f4ffed 100644
--- a/templates/user_responses.html
+++ b/forum/skins/default/templates/user_responses.html
diff --git a/templates/user_stats.html b/forum/skins/default/templates/user_stats.html
index ecc39807..a3f88131 100644
--- a/templates/user_stats.html
+++ b/forum/skins/default/templates/user_stats.html
@@ -69,12 +69,12 @@
<table>
<tr>
<td width="60">
- <img style="cursor: default;" src="{% href "/content/images/vote-arrow-up-on.png" %}" alt="{% trans "thumb up" %}" />
+ <img style="cursor: default;" src="{% media "/media/images/vote-arrow-up-on.png" %}" alt="{% trans "thumb up" %}" />
<span title="{% trans "user has voted up this many times" %}" class="vote-count">{{up_votes}}</span>
</td>
<td width="60">
- <img style="cursor: default;" src="{% href "/content/images/vote-arrow-down-on.png" %}" alt="{% trans "thumb down" %}" />
+ <img style="cursor: default;" src="{% media "/media/images/vote-arrow-down-on.png" %}" alt="{% trans "thumb down" %}" />
<span title="{% trans "user voted down this many times" %}" class="vote-count">{{down_votes}}</span>
</td>
@@ -98,7 +98,7 @@
{% for tag in user_tags%}
<a rel="tag"
title="{% blocktrans with tag.name as tag_name %}see other questions with {{view_user}}'s contributions tagged '{{ tag_name }}' {% endblocktrans %}"
- href="{% url forum.views.tag tag|urlencode %}?user={{view_user.username}}">{{tag.name}}</a>
+ href="{% url tag_questions tag|urlencode %}?user={{view_user.username}}">{{tag.name}}</a>
<span class="tag-number">&#215; {{ tag.user_tag_usage_count|intcomma }}</span><br/>
{% if forloop.counter|divisibleby:"10" %}
</td>
diff --git a/templates/user_tabs.html b/forum/skins/default/templates/user_tabs.html
index 908e8430..908e8430 100644
--- a/templates/user_tabs.html
+++ b/forum/skins/default/templates/user_tabs.html
diff --git a/templates/user_votes.html b/forum/skins/default/templates/user_votes.html
index 94d7fcbd..b56aab01 100644
--- a/templates/user_votes.html
+++ b/forum/skins/default/templates/user_votes.html
@@ -12,9 +12,9 @@
<div style="width:150px;float:left">{% diff_date vote.voted_at 3 %}</div>
<div style="width:30px;float:left">
{% ifequal vote.vote 1 %}
- <img src="{% href "/content/images/vote-arrow-up-on.png" %}" title="{% trans "upvote" %}">
+ <img src="{% media "/media/images/vote-arrow-up-on.png" %}" title="{% trans "upvote" %}">
{% else %}
- <img src="{% href "/content/images/vote-arrow-down-on.png" %}" title="{% trans "downvote" %}">
+ <img src="{% media "/media/images/vote-arrow-down-on.png" %}" title="{% trans "downvote" %}">
{% endifequal %}
</div>
<div style="float:left;overflow:hidden;width:750px">
diff --git a/templates/users.html b/forum/skins/default/templates/users.html
index 3a59b0c0..3a59b0c0 100644
--- a/templates/users.html
+++ b/forum/skins/default/templates/users.html
diff --git a/templates/users_questions.html b/forum/skins/default/templates/users_questions.html
index b445a74c..8049d832 100644
--- a/templates/users_questions.html
+++ b/forum/skins/default/templates/users_questions.html
@@ -10,14 +10,14 @@
<div class="favorites-count">
<img title="{% trans "this questions was selected as favorite" %} {{question.favourite_count}} {% trans "number of times" %}"
alt="{% trans "thumb-up on" %}"
- src="{% href "/content/images/vote-favorite-on.png" %}"/>
+ src="{% media "/media/images/vote-favorite-on.png" %}"/>
<div><b>{{question.favourite_count|intcomma}}</b></div>
</div>
{% else %}
<div class="favorites-count-off">
<img title="{% trans "this question was selected as favorite" %}{{question.favourite_count}} {% trans "number of times" %}"
alt="{% trans "thumb-up off" %}"
- src="{% href "/content/images/vote-favorite-off.png" %}"/>
+ src="{% media "/media/images/vote-favorite-off.png" %}"/>
<div><b>{{question.favourite_count|intcomma}}</b></div>
</div>
{% endif %}
@@ -49,7 +49,7 @@
{% convert2tagname_list question %}
{% for tag in question.tagnames %}
<!--todo - move trans below to blocktrans -->
- <a href="{% url forum.views.tag tag|urlencode %}" title="{% trans "see questions tagged" %} '{{ tag }}' {% trans "using tags" %}" rel="tag">{{ tag }}</a>
+ <a href="{% url tag_questions tag|urlencode %}" title="{% trans "see questions tagged" %} '{{ tag }}' {% trans "using tags" %}" rel="tag">{{ tag }}</a>
{% endfor %}
</div>
<div class="started">
diff --git a/forum/templatetags/extra_filters.py b/forum/templatetags/extra_filters.py
index 22ec0109..3644fdc3 100644
--- a/forum/templatetags/extra_filters.py
+++ b/forum/templatetags/extra_filters.py
@@ -1,5 +1,4 @@
from django import template
-from django.core import serializers
from forum import auth
import logging
@@ -92,7 +91,3 @@ def cnprog_intword(number):
return number
except:
return number
-
-@register.filter
-def json_serialize(object):
- return serializers.serialize('json',object)
diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py
index 4f79e497..26c52b8d 100644
--- a/forum/templatetags/extra_tags.py
+++ b/forum/templatetags/extra_tags.py
@@ -13,6 +13,7 @@ from forum.models import Question, Answer, QuestionRevision, AnswerRevision
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
from django.conf import settings
+from forum import skins
register = template.Library()
@@ -276,9 +277,11 @@ def get_latest_changed_timestamp():
return timestr
@register.simple_tag
-def href(url):
- url = '///' + settings.FORUM_SCRIPT_ALIAS + '/' + url
- return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
+def media(url):
+ url = skins.find_media_source(url)
+ if url:
+ url = '///' + settings.FORUM_SCRIPT_ALIAS + '/m/' + url
+ return posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
class ItemSeparatorNode(template.Node):
def __init__(self,separator):
@@ -323,29 +326,32 @@ def joinitems(parser,token):
return JoinItemListNode(separator=sep_node,items=nodelist)
-class BlockResourceNode(template.Node):
+class BlockMediaUrlNode(template.Node):
def __init__(self,nodelist):
self.items = nodelist
def render(self,context):
- out = '///' + settings.FORUM_SCRIPT_ALIAS
+ prefix = '///' + settings.FORUM_SCRIPT_ALIAS + 'm/'
+ url = ''
if self.items:
- out += '/'
+ url += '/'
for item in self.items:
- bit = item.render(context)
- out += bit
- out = os.path.normpath(out) + '?v=%d' % settings.RESOURCE_REVISION
+ url += item.render(context)
+
+ url = skins.find_media_source(url)
+ url = prefix + url
+ out = posixpath.normpath(url) + '?v=%d' % settings.RESOURCE_REVISION
return out.replace(' ','')
-@register.tag(name='blockresource')
-def blockresource(parser,token):
+@register.tag(name='blockmedia')
+def blockmedia(parser,token):
try:
tagname = token.split_contents()
except ValueError:
- raise template.TemplateSyntaxError("blockresource tag does not use arguments")
+ raise template.TemplateSyntaxError("blockmedia tag does not use arguments")
nodelist = []
while True:
- nodelist.append(parser.parse(('endblockresource')))
+ nodelist.append(parser.parse(('endblockmedia')))
next = parser.next_token()
- if next.contents == 'endblockresource':
+ if next.contents == 'endblockmedia':
break
- return BlockResourceNode(nodelist)
+ return BlockMediaUrlNode(nodelist)
diff --git a/forum/upfiles/README b/forum/upfiles/README
new file mode 100644
index 00000000..17bf8ecb
--- /dev/null
+++ b/forum/upfiles/README
@@ -0,0 +1,2 @@
+This directory is to contain uploaded images and other files
+must be writable by the webserver
diff --git a/forum/urls.py b/forum/urls.py
index 42746d44..fd9ebdc1 100644
--- a/forum/urls.py
+++ b/forum/urls.py
@@ -5,6 +5,7 @@ from forum import views as app
from forum.feed import RssLastestQuestionsFeed
from forum.sitemap import QuestionsSitemap
from django.utils.translation import ugettext as _
+import logging
admin.autodiscover()
feeds = {
@@ -14,79 +15,88 @@ sitemaps = {
'questions': QuestionsSitemap
}
-APP_PATH = os.path.dirname(os.path.dirname(__file__))
+APP_PATH = os.path.dirname(__file__)
urlpatterns = patterns('',
- url(r'^$', app.index, name='index'),
- url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
- (r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/content/images/favicon.ico'}),
- (r'^favicon\.gif$', 'django.views.generic.simple.redirect_to', {'url': '/content/images/favicon.gif'}),
- (r'^content/(?P<path>.*)$', 'django.views.static.serve',
- {'document_root': os.path.join(APP_PATH, 'templates/content').replace('\\','/')}
+ url(r'^$', app.readers.index, name='index'),
+ url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}, name='sitemap'),
+ #(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/media/images/favicon.ico'}),
+ #(r'^favicon\.gif$', 'django.views.generic.simple.redirect_to', {'url': '/media/images/favicon.gif'}),
+ url(r'^m/(?P<path>.*)$', 'django.views.static.serve',
+ {'document_root': os.path.join(APP_PATH,'skins').replace('\\','/')},
+ name='osqa_media',
),
- (r'^%s(?P<path>.*)$' % _('upfiles/'), 'django.views.static.serve',
- {'document_root': os.path.join(APP_PATH, 'templates/upfiles').replace('\\','/')}
+ url(r'^%s(?P<path>.*)$' % _('upfiles/'), 'django.views.static.serve',
+ {'document_root': os.path.join(APP_PATH,'upfiles').replace('\\','/')},
+ name='uploaded_file',
),
- (r'^%s/$' % _('signin/'), 'django_authopenid.views.signin'),
- url(r'^%s$' % _('about/'), app.about, name='about'),
- url(r'^%s$' % _('faq/'), app.faq, name='faq'),
- url(r'^%s$' % _('privacy/'), app.privacy, name='privacy'),
- url(r'^%s$' % _('logout/'), app.logout, name='logout'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('comments/')), app.answer_comments, name='answer_comments'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('edit/')), app.edit_answer, name='edit_answer'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('revisions/')), app.answer_revisions, name='answer_revisions'),
- url(r'^%s$' % _('questions/'), app.questions, name='questions'),
- url(r'^%s%s$' % (_('questions/'), _('ask/')), app.ask, name='ask'),
- url(r'^%s%s$' % (_('questions/'), _('unanswered/')), app.unanswered, name='unanswered'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('edit/')), app.edit_question, name='edit_question'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('close/')), app.close, name='close'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('reopen/')), app.reopen, name='reopen'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('answer/')), app.answer, name='answer'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')), app.vote, name='vote'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), app.question_revisions, name='question_revisions'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')), app.question_comments, name='question_comments'),
- url(r'^%s$' % _('command/'), app.ajax_command, name='call_ajax'),
+ 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'),
+ url(r'^%s$' % _('logout/'), app.meta.logout, name='logout'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('comments/')), app.writers.answer_comments, name='answer_comments'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('edit/')), app.writers.edit_answer, name='edit_answer'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('revisions/')), app.readers.answer_revisions, name='answer_revisions'),
+ url(r'^%s$' % _('questions/'), app.readers.questions, name='questions'),
+ url(r'^%s%s$' % (_('questions/'), _('ask/')), app.writers.ask, name='ask'),
+ url(r'^%s%s$' % (_('questions/'), _('unanswered/')), app.readers.unanswered, name='unanswered'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('edit/')), app.writers.edit_question, name='edit_question'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('close/')), app.commands.close, name='close'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('reopen/')), app.commands.reopen, name='reopen'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('answer/')), app.writers.answer, name='answer'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')), app.commands.vote, name='vote'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), app.readers.question_revisions, name='question_revisions'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')), app.writers.question_comments, name='question_comments'),
+ url(r'^%s$' % _('command/'), app.commands.ajax_command, name='call_ajax'),
url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('questions/'), _('comments/'),_('delete/')), \
- app.delete_comment, kwargs={'commented_object_type':'question'},\
+ app.writers.delete_comment, kwargs={'commented_object_type':'question'},\
name='delete_question_comment'),
url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('answers/'), _('comments/'),_('delete/')), \
- app.delete_comment, kwargs={'commented_object_type':'answer'}, \
+ app.writers.delete_comment, kwargs={'commented_object_type':'answer'}, \
name='delete_answer_comment'), \
#place general question item in the end of other operations
- url(r'^%s(?P<id>\d+)/' % _('question/'), app.question, name='question'),
- url(r'^%s$' % _('tags/'), app.tags, name='tags'),
- url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.tag, name='tag_questions'),
+ url(r'^%s(?P<id>\d+)/' % _('question/'), app.readers.question, name='question'),
+ url(r'^%s$' % _('tags/'), app.readers.tags, name='tags'),
+ url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.readers.tag, name='tag_questions'),
- url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')), app.mark_tag, \
+ url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')), app.commands.mark_tag, \
kwargs={'reason':'good','action':'add'}, \
name='mark_interesting_tag'),
- url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('ignored/')), app.mark_tag, \
+ url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('ignored/')), app.commands.mark_tag, \
kwargs={'reason':'bad','action':'add'}, \
name='mark_ignored_tag'),
- url(r'^%s(?P<tag>[^/]+)/$' % _('unmark-tag/'), app.mark_tag, \
+ url(r'^%s(?P<tag>[^/]+)/$' % _('unmark-tag/'), app.commands.mark_tag, \
kwargs={'action':'remove'}, \
name='mark_ignored_tag'),
- url(r'^%s$' % _('users/'),app.users, name='users'),
- url(r'^%s(?P<id>\d+)/$' % _('moderate-user/'), app.moderate_user, name='moderate_user'),
- url(r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')), app.edit_user, name='edit_user'),
- url(r'^%s(?P<id>\d+)//*' % _('users/'), app.user, name='user'),
- url(r'^%s$' % _('badges/'),app.badges, name='badges'),
- url(r'^%s(?P<id>\d+)//*' % _('badges/'), app.badge, name='badge'),
- url(r'^%s%s$' % (_('messages/'), _('markread/')),app.read_message, name='read_message'),
+ url(r'^%s$' % _('users/'),app.users.users, name='users'),
+ url(r'^%s(?P<id>\d+)/$' % _('moderate-user/'), app.users.moderate_user, name='moderate_user'),
+ url(r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')), app.users.edit_user, name='edit_user'),
+ url(r'^%s(?P<id>\d+)//*' % _('users/'), app.users.user, name='user'),
+ url(r'^%s$' % _('badges/'),app.meta.badges, name='badges'),
+ url(r'^%s(?P<id>\d+)//*' % _('badges/'), app.meta.badge, name='badge'),
+ url(r'^%s%s$' % (_('messages/'), _('markread/')),app.commands.read_message, name='read_message'),
# (r'^admin/doc/' % _('admin/doc'), include('django.contrib.admindocs.urls')),
- (r'^%s(.*)' % _('nimda/'), admin.site.root),
- url(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}),
- (r'^%s$' % _('upload/'), app.upload),
- url(r'^%s$' % _('books/'), app.books, name='books'),
- url(r'^%s%s(?P<short_name>[^/]+)/$' % (_('books/'), _('ask/')), app.ask_book, name='ask_book'),
- url(r'^%s(?P<short_name>[^/]+)/$' % _('books/'), app.book, name='book'),
- url(r'^%s$' % _('search/'), app.search, name='search'),
- url(r'^%s$' % _('feedback/'), app.feedback, name='feedback'),
+ url(r'^%s(.*)' % _('nimda/'), admin.site.root, name='osqa_admin'),
+ url(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}, name='feeds'),
+ 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'^i18n/', include('django.conf.urls.i18n')),
)
+
+from forum.modules import get_modules_script
+
+module_patterns = get_modules_script('urls')
+
+for pattern_file in module_patterns:
+ pattern = getattr(pattern_file, 'urlpatterns', None)
+ if pattern:
+ urlpatterns += pattern
+
diff --git a/forum/user.py b/forum/user.py
deleted file mode 100644
index 40bf6a89..00000000
--- a/forum/user.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from django.utils.translation import ugettext as _
-class UserView:
- def __init__(self, id, tab_title, tab_description, page_title, view_name, template_file, data_size=0):
- self.id = id
- self.tab_title = tab_title
- self.tab_description = tab_description
- self.page_title = page_title
- self.view_name = view_name
- self.template_file = template_file
- self.data_size = data_size
-
-
-USER_TEMPLATE_VIEWS = (
- UserView(
- id = 'stats',
- tab_title = _('overview'),
- tab_description = _('user profile'),
- page_title = _('user profile overview'),
- view_name = 'user_stats',
- template_file = 'user_stats.html'
- ),
- UserView(
- id = 'recent',
- tab_title = _('recent activity'),
- tab_description = _('recent user activity'),
- page_title = _('profile - recent activity'),
- view_name = 'user_recent',
- template_file = 'user_recent.html',
- data_size = 50
- ),
- UserView(
- id = 'responses',
- tab_title = _('responses'),
- tab_description = _('comments and answers to others questions'),
- page_title = _('profile - responses'),
- view_name = 'user_responses',
- template_file = 'user_responses.html',
- data_size = 50
- ),
- UserView(
- id = 'reputation',
- tab_title = _('reputation'),
- tab_description = _('user reputation in the community'),
- page_title = _('profile - user reputation'),
- view_name = 'user_reputation',
- template_file = 'user_reputation.html'
- ),
- UserView(
- id = 'favorites',
- tab_title = _('favorite questions'),
- tab_description = _('users favorite questions'),
- page_title = _('profile - favorite questions'),
- view_name = 'user_favorites',
- template_file = 'user_favorites.html',
- data_size = 50
- ),
- UserView(
- id = 'votes',
- tab_title = _('casted votes'),
- tab_description = _('user vote record'),
- page_title = _('profile - votes'),
- view_name = 'user_votes',
- template_file = 'user_votes.html',
- data_size = 50
- ),
- UserView(
- id = 'email_subscriptions',
- tab_title = _('email subscriptions'),
- tab_description = _('email subscription settings'),
- page_title = _('profile - email subscriptions'),
- view_name = 'user_email_subscriptions',
- template_file = 'user_email_subscriptions.html'
- )
-)
diff --git a/user_messages/__init__.py b/forum/user_messages/__init__.py
index 0136c888..0136c888 100644
--- a/user_messages/__init__.py
+++ b/forum/user_messages/__init__.py
diff --git a/user_messages/context_processors.py b/forum/user_messages/context_processors.py
index 894f5801..2bf26269 100644
--- a/user_messages/context_processors.py
+++ b/forum/user_messages/context_processors.py
@@ -6,7 +6,7 @@ Time-stamp: <2008-07-19 23:16:19 carljm context_processors.py>
"""
from django.utils.encoding import StrAndUnicode
-from user_messages import get_and_delete_messages
+from forum.user_messages import get_and_delete_messages
def user_messages (request):
"""
diff --git a/pgfulltext/__init__.py b/forum/utils/__init__.py
index e69de29b..e69de29b 100644
--- a/pgfulltext/__init__.py
+++ b/forum/utils/__init__.py
diff --git a/utils/cache.py b/forum/utils/cache.py
index 410c0662..410c0662 100644
--- a/utils/cache.py
+++ b/forum/utils/cache.py
diff --git a/utils/decorators.py b/forum/utils/decorators.py
index e4e7acb3..e4e7acb3 100644
--- a/utils/decorators.py
+++ b/forum/utils/decorators.py
diff --git a/forum/diff.py b/forum/utils/diff.py
index d741d788..d741d788 100644
--- a/forum/diff.py
+++ b/forum/utils/diff.py
diff --git a/utils/forms.py b/forum/utils/forms.py
index c54056ca..c54056ca 100644
--- a/utils/forms.py
+++ b/forum/utils/forms.py
diff --git a/utils/html.py b/forum/utils/html.py
index 25a74a4a..25a74a4a 100644
--- a/utils/html.py
+++ b/forum/utils/html.py
diff --git a/utils/lists.py b/forum/utils/lists.py
index bbcfae98..bbcfae98 100644
--- a/utils/lists.py
+++ b/forum/utils/lists.py
diff --git a/utils/odict.py b/forum/utils/odict.py
index 2c8391d7..2c8391d7 100644
--- a/utils/odict.py
+++ b/forum/utils/odict.py
diff --git a/forum/views.py b/forum/views.py
deleted file mode 100644
index 4d214bad..00000000
--- a/forum/views.py
+++ /dev/null
@@ -1,2412 +0,0 @@
-# encoding:utf-8
-import os.path
-import time, datetime, calendar, random
-import logging
-from urllib import quote, unquote
-from django.conf import settings
-from django.core.files.storage import default_storage
-from django.shortcuts import render_to_response, get_object_or_404
-from django.contrib.auth.decorators import login_required
-from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
-from django.core.paginator import Paginator, EmptyPage, InvalidPage
-from django.template import RequestContext, loader
-from django.utils.html import *
-from django.utils import simplejson
-from django.core import serializers
-from django.core.mail import mail_admins
-from django.db import transaction
-from django.db.models import Count, Q
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext as _
-from django.utils.datastructures import SortedDict
-from django.template.defaultfilters import slugify
-from django.core.exceptions import PermissionDenied
-
-from utils.html import sanitize_html
-from utils.decorators import ajax_method, ajax_login_required
-from markdown2 import Markdown
-#from lxml.html.diff import htmldiff
-from forum.diff import textDiff as htmldiff
-from forum.forms import *
-from forum.models import *
-from forum.auth import *
-from forum.const import *
-from forum.user import *
-from forum import auth
-from utils.forms import get_next_url
-
-# used in index page
-INDEX_PAGE_SIZE = 20
-INDEX_AWARD_SIZE = 15
-INDEX_TAGS_SIZE = 100
-# used in tags list
-DEFAULT_PAGE_SIZE = 60
-# used in questions
-QUESTIONS_PAGE_SIZE = 10
-# used in users
-USERS_PAGE_SIZE = 35
-# used in answers
-ANSWERS_PAGE_SIZE = 10
-markdowner = Markdown(html4tags=True)
-question_type = ContentType.objects.get_for_model(Question)
-answer_type = ContentType.objects.get_for_model(Answer)
-comment_type = ContentType.objects.get_for_model(Comment)
-question_revision_type = ContentType.objects.get_for_model(QuestionRevision)
-answer_revision_type = ContentType.objects.get_for_model(AnswerRevision)
-repute_type = ContentType.objects.get_for_model(Repute)
-question_type_id = question_type.id
-answer_type_id = answer_type.id
-comment_type_id = comment_type.id
-question_revision_type_id = question_revision_type.id
-answer_revision_type_id = answer_revision_type.id
-repute_type_id = repute_type.id
-def _get_tags_cache_json():
- tags = Tag.objects.filter(deleted=False).all()
- tags_list = []
- for tag in tags:
- dic = {'n': tag.name, 'c': tag.used_count}
- tags_list.append(dic)
- tags = simplejson.dumps(tags_list)
- return tags
-
-def _get_and_remember_questions_sort_method(request, view_dic, default):
- if default not in view_dic:
- raise Exception('default value must be in view_dic')
-
- q_sort_method = request.REQUEST.get('sort', None)
- if q_sort_method == None:
- q_sort_method = request.session.get('questions_sort_method', default)
-
- if q_sort_method not in view_dic:
- q_sort_method = default
- request.session['questions_sort_method'] = q_sort_method
- return q_sort_method, view_dic[q_sort_method]
-
-def index(request):
- view_dic = {
- "latest":"-last_activity_at",
- "hottest":"-answer_count",
- "mostvoted":"-score",
- }
- view_id, orderby = _get_and_remember_questions_sort_method(request, view_dic, 'latest')
-
- page_size = request.session.get('pagesize', QUESTIONS_PAGE_SIZE)
- questions = Question.objects.exclude(deleted=True).order_by(orderby)[:page_size]
- # RISK - inner join queries
- questions = questions.select_related()
- tags = Tag.objects.get_valid_tags(INDEX_TAGS_SIZE)
-
- awards = Award.objects.get_recent_awards()
-
- (interesting_tag_names, ignored_tag_names) = (None, None)
- if request.user.is_authenticated():
- pt = MarkedTag.objects.filter(user=request.user)
- interesting_tag_names = pt.filter(reason='good').values_list('tag__name', flat=True)
- ignored_tag_names = pt.filter(reason='bad').values_list('tag__name', flat=True)
-
- tags_autocomplete = _get_tags_cache_json()
-
- return render_to_response('index.html', {
- 'interesting_tag_names': interesting_tag_names,
- 'tags_autocomplete': tags_autocomplete,
- 'ignored_tag_names': ignored_tag_names,
- "questions" : questions,
- "tab_id" : view_id,
- "tags" : tags,
- "awards" : awards[:INDEX_AWARD_SIZE],
- }, context_instance=RequestContext(request))
-
-def about(request):
- return render_to_response('about.html', context_instance=RequestContext(request))
-
-def faq(request):
- data = {
- 'gravatar_faq_url': reverse('faq') + '#gravatar',
- 'send_email_key_url': reverse('send_email_key'),
- 'ask_question_url': reverse('ask'),
- }
- return render_to_response('faq.html', data, context_instance=RequestContext(request))
-
-def feedback(request):
- data = {}
- form = None
- if request.method == "POST":
- form = FeedbackForm(request.POST)
- if form.is_valid():
- if not request.user.is_authenticated:
- data['email'] = form.cleaned_data.get('email',None)
- data['message'] = form.cleaned_data['message']
- data['name'] = form.cleaned_data.get('name',None)
- message = render_to_response('feedback_email.txt',data,context_instance=RequestContext(request))
- mail_admins(_('Q&A forum feedback'), message)
- msg = _('Thanks for the feedback!')
- request.user.message_set.create(message=msg)
- return HttpResponseRedirect(get_next_url(request))
- else:
- form = FeedbackForm(initial={'next':get_next_url(request)})
-
- data['form'] = form
- return render_to_response('feedback.html', data, context_instance=RequestContext(request))
-feedback.CANCEL_MESSAGE=_('We look forward to hearing your feedback! Please, give it next time :)')
-
-def privacy(request):
- return render_to_response('privacy.html', context_instance=RequestContext(request))
-
-def unanswered(request):
- return questions(request, unanswered=True)
-
-def questions(request, tagname=None, unanswered=False):
- """
- List of Questions, Tagged questions, and Unanswered questions.
- """
- # template file
- # "questions.html" or maybe index.html in the future
- template_file = "questions.html"
- # 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",10)
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
-
- view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
- view_id, orderby = _get_and_remember_questions_sort_method(request,view_dic,'latest')
-
- # check if request is from tagged questions
- qs = Question.objects.exclude(deleted=True)
-
- if tagname is not None:
- qs = qs.filter(tags__name = unquote(tagname))
-
- if unanswered:
- qs = qs.exclude(answer_accepted=True)
-
- author_name = None
- #user contributed questions & answers
- if 'user' in request.GET:
- try:
- author_name = request.GET['user']
- u = User.objects.get(username=author_name)
- qs = qs.filter(Q(author=u) | Q(answers__author=u))
- except User.DoesNotExist:
- author_name = None
-
- if request.user.is_authenticated():
- uid_str = str(request.user.id)
- qs = qs.extra(
- select = SortedDict([
- (
- 'interesting_score',
- 'SELECT COUNT(1) FROM forum_markedtag, question_tags '
- + 'WHERE forum_markedtag.user_id = %s '
- + 'AND forum_markedtag.tag_id = question_tags.tag_id '
- + 'AND forum_markedtag.reason = \'good\' '
- + 'AND question_tags.question_id = question.id'
- ),
- ]),
- select_params = (uid_str,),
- )
- if request.user.hide_ignored_questions:
- ignored_tags = Tag.objects.filter(user_selections__reason='bad',
- user_selections__user = request.user)
- qs = qs.exclude(tags__in=ignored_tags)
- else:
- qs = qs.extra(
- select = SortedDict([
- (
- 'ignored_score',
- 'SELECT COUNT(1) FROM forum_markedtag, question_tags '
- + 'WHERE forum_markedtag.user_id = %s '
- + 'AND forum_markedtag.tag_id = question_tags.tag_id '
- + 'AND forum_markedtag.reason = \'bad\' '
- + 'AND question_tags.question_id = question.id'
- )
- ]),
- select_params = (uid_str, )
- )
-
- qs = qs.select_related(depth=1).order_by(orderby)
-
- objects_list = Paginator(qs, pagesize)
- questions = objects_list.page(page)
-
- # Get related tags from this page objects
- if questions.object_list.count() > 0:
- related_tags = Tag.objects.get_tags_by_questions(questions.object_list)
- else:
- related_tags = None
- tags_autocomplete = _get_tags_cache_json()
-
- # get the list of interesting and ignored tags
- (interesting_tag_names, ignored_tag_names) = (None, None)
- if request.user.is_authenticated():
- pt = MarkedTag.objects.filter(user=request.user)
- interesting_tag_names = pt.filter(reason='good').values_list('tag__name', flat=True)
- ignored_tag_names = pt.filter(reason='bad').values_list('tag__name', flat=True)
-
- return render_to_response(template_file, {
- "questions" : questions,
- "author_name" : author_name,
- "tab_id" : view_id,
- "questions_count" : objects_list.count,
- "tags" : related_tags,
- "tags_autocomplete" : tags_autocomplete,
- "searchtag" : tagname,
- "is_unanswered" : unanswered,
- "interesting_tag_names": interesting_tag_names,
- 'ignored_tag_names': ignored_tag_names,
- "context" : {
- 'is_paginated' : True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': questions.has_previous(),
- 'has_next': questions.has_next(),
- 'previous': questions.previous_page_number(),
- 'next': questions.next_page_number(),
- 'base_url' : request.path + '?sort=%s&' % view_id,
- 'pagesize' : pagesize
- }}, context_instance=RequestContext(request))
-
-def create_new_answer( question=None, author=None,\
- added_at=None, wiki=False,\
- text='', email_notify=False):
-
- html = sanitize_html(markdowner.convert(text))
-
- #create answer
- answer = Answer(
- question = question,
- author = author,
- added_at = added_at,
- wiki = wiki,
- html = html
- )
- if answer.wiki:
- answer.last_edited_by = answer.author
- answer.last_edited_at = added_at
- answer.wikified_at = added_at
-
- answer.save()
-
- #update question data
- question.last_activity_at = added_at
- question.last_activity_by = author
- question.save()
- Question.objects.update_answer_count(question)
-
- #update revision
- AnswerRevision.objects.create(
- answer = answer,
- revision = 1,
- author = author,
- revised_at = added_at,
- summary = CONST['default_version'],
- text = text
- )
-
- #set notification/delete
- if email_notify:
- if author not in question.followed_by.all():
- question.followed_by.add(author)
- else:
- #not sure if this is necessary. ajax should take care of this...
- try:
- question.followed_by.remove(author)
- except:
- pass
-
-def create_new_question(title=None,author=None,added_at=None,
- wiki=False,tagnames=None,summary=None,
- text=None):
- """this is not a view
- and maybe should become one of the methods on Question object?
- """
- html = sanitize_html(markdowner.convert(text))
- question = Question(
- title = title,
- author = author,
- added_at = added_at,
- last_activity_at = added_at,
- last_activity_by = author,
- wiki = wiki,
- tagnames = tagnames,
- html = html,
- summary = summary
- )
- if question.wiki:
- question.last_edited_by = question.author
- question.last_edited_at = added_at
- question.wikified_at = added_at
-
- question.save()
-
- # create the first revision
- QuestionRevision.objects.create(
- question = question,
- revision = 1,
- title = question.title,
- author = author,
- revised_at = added_at,
- tagnames = question.tagnames,
- summary = CONST['default_version'],
- text = text
- )
- return question
-
-#TODO: allow anynomus user to ask question by providing email and username.
-#@login_required
-def ask(request):
- if request.method == "POST":
- form = AskForm(request.POST)
- if form.is_valid():
-
- added_at = datetime.datetime.now()
- title = strip_tags(form.cleaned_data['title'].strip())
- wiki = form.cleaned_data['wiki']
- tagnames = form.cleaned_data['tags'].strip()
- text = form.cleaned_data['text']
- html = sanitize_html(markdowner.convert(text))
- summary = strip_tags(html)[:120]
-
- if request.user.is_authenticated():
- author = request.user
-
- question = create_new_question(
- title = title,
- author = author,
- added_at = added_at,
- wiki = wiki,
- tagnames = tagnames,
- summary = summary,
- text = text
- )
-
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- request.session.flush()
- session_key = request.session.session_key
- question = AnonymousQuestion(
- session_key = session_key,
- title = title,
- tagnames = tagnames,
- wiki = wiki,
- text = text,
- summary = summary,
- added_at = added_at,
- ip_addr = request.META['REMOTE_ADDR'],
- )
- question.save()
- return HttpResponseRedirect(reverse('user_signin_new_question'))
- else:
- form = AskForm()
-
- tags = _get_tags_cache_json()
- return render_to_response('ask.html', {
- 'form' : form,
- 'tags' : tags,
- 'email_validation_faq_url':reverse('faq') + '#validate',
- }, context_instance=RequestContext(request))
-
-def question(request, id):
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
-
- view_id = request.GET.get('sort', None)
- view_dic = {"latest":"-added_at", "oldest":"added_at", "votes":"-score" }
- try:
- orderby = view_dic[view_id]
- except KeyError:
- qsm = request.session.get('questions_sort_method',None)
- if qsm in ('mostvoted','latest'):
- logging.debug('loaded from session ' + qsm)
- if qsm == 'mostvoted':
- view_id = 'votes'
- orderby = '-score'
- else:
- view_id = 'latest'
- orderby = '-added_at'
- else:
- view_id = "votes"
- orderby = "-score"
-
- logging.debug('view_id=' + str(view_id))
-
- question = get_object_or_404(Question, id=id)
- try:
- pattern = r'/%s%s%d/([\w-]+)' % (settings.FORUM_SCRIPT_ALIAS,_('question/'), question.id)
- path_re = re.compile(pattern)
- logging.debug(pattern)
- logging.debug(request.path)
- m = path_re.match(request.path)
- if m:
- slug = m.group(1)
- logging.debug('have slug %s' % slug)
- assert(slug == slugify(question.title))
- else:
- logging.debug('no match!')
- except:
- return HttpResponseRedirect(question.get_absolute_url())
-
- if question.deleted and not can_view_deleted_post(request.user, question):
- raise Http404
- answer_form = AnswerForm(question,request.user)
- answers = Answer.objects.get_answers_from_question(question, request.user)
- answers = answers.select_related(depth=1)
-
- favorited = question.has_favorite_by_user(request.user)
- if request.user.is_authenticated():
- question_vote = question.votes.select_related().filter(user=request.user)
- else:
- question_vote = None #is this correct?
- if question_vote is not None and question_vote.count() > 0:
- question_vote = question_vote[0]
-
- user_answer_votes = {}
- for answer in answers:
- vote = answer.get_user_vote(request.user)
- if vote is not None and not user_answer_votes.has_key(answer.id):
- vote_value = -1
- if vote.is_upvote():
- vote_value = 1
- user_answer_votes[answer.id] = vote_value
-
- if answers is not None:
- answers = answers.order_by("-accepted", orderby)
-
- filtered_answers = []
- for answer in answers:
- if answer.deleted == True:
- if answer.author_id == request.user.id:
- filtered_answers.append(answer)
- else:
- filtered_answers.append(answer)
-
- objects_list = Paginator(filtered_answers, ANSWERS_PAGE_SIZE)
- page_objects = objects_list.page(page)
-
- #todo: merge view counts per user and per session
- #1) view count per session
- update_view_count = False
- if 'question_view_times' not in request.session:
- request.session['question_view_times'] = {}
-
- last_seen = request.session['question_view_times'].get(question.id,None)
- updated_when, updated_who = question.get_last_update_info()
-
- if updated_who != request.user:
- if last_seen:
- if last_seen < updated_when:
- update_view_count = True
- else:
- update_view_count = True
-
- request.session['question_view_times'][question.id] = datetime.datetime.now()
-
- if update_view_count:
- question.view_count += 1
- question.save()
-
- #2) question view count per user
- if request.user.is_authenticated():
- try:
- question_view = QuestionView.objects.get(who=request.user, question=question)
- except QuestionView.DoesNotExist:
- question_view = QuestionView(who=request.user, question=question)
- question_view.when = datetime.datetime.now()
- question_view.save()
-
- return render_to_response('question.html', {
- "question" : question,
- "question_vote" : question_vote,
- "question_comment_count":question.comments.count(),
- "answer" : answer_form,
- "answers" : page_objects.object_list,
- "user_answer_votes": user_answer_votes,
- "tags" : question.tags.all(),
- "tab_id" : view_id,
- "favorited" : favorited,
- "similar_questions" : Question.objects.get_similar_questions(question),
- "context" : {
- 'is_paginated' : True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': page_objects.has_previous(),
- 'has_next': page_objects.has_next(),
- 'previous': page_objects.previous_page_number(),
- 'next': page_objects.next_page_number(),
- 'base_url' : request.path + '?sort=%s&' % view_id,
- 'extend_url' : "#sort-top"
- }
- }, context_instance=RequestContext(request))
-
-@login_required
-def close(request, id):
- question = get_object_or_404(Question, id=id)
- if not can_close_question(request.user, question):
- return HttpResponse('Permission denied.')
- if request.method == 'POST':
- form = CloseForm(request.POST)
- if form.is_valid():
- reason = form.cleaned_data['reason']
- question.closed = True
- question.closed_by = request.user
- question.closed_at = datetime.datetime.now()
- question.close_reason = reason
- question.save()
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- form = CloseForm()
- return render_to_response('close.html', {
- 'form' : form,
- 'question' : question,
- }, context_instance=RequestContext(request))
-
-@login_required
-def reopen(request, id):
- question = get_object_or_404(Question, id=id)
- # open question
- if not can_reopen_question(request.user, question):
- return HttpResponse('Permission denied.')
- if request.method == 'POST' :
- Question.objects.filter(id=question.id).update(closed=False,
- closed_by=None, closed_at=None, close_reason=None)
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- return render_to_response('reopen.html', {
- 'question' : question,
- }, context_instance=RequestContext(request))
-
-@login_required
-def edit_question(request, id):
- question = get_object_or_404(Question, id=id)
- if question.deleted and not can_view_deleted_post(request.user, question):
- raise Http404
- if can_edit_post(request.user, question):
- return _edit_question(request, question)
- elif can_retag_questions(request.user):
- return _retag_question(request, question)
- else:
- raise Http404
-
-def _retag_question(request, question):
- if request.method == 'POST':
- form = RetagQuestionForm(question, request.POST)
- if form.is_valid():
- if form.has_changed():
- latest_revision = question.get_latest_revision()
- retagged_at = datetime.datetime.now()
- # Update the Question itself
- Question.objects.filter(id=question.id).update(
- tagnames = form.cleaned_data['tags'],
- last_edited_at = retagged_at,
- last_edited_by = request.user,
- last_activity_at = retagged_at,
- last_activity_by = request.user
- )
- # Update the Question's tag associations
- tags_updated = Question.objects.update_tags(question,
- form.cleaned_data['tags'], request.user)
- # Create a new revision
- QuestionRevision.objects.create(
- question = question,
- title = latest_revision.title,
- author = request.user,
- revised_at = retagged_at,
- tagnames = form.cleaned_data['tags'],
- summary = CONST['retagged'],
- text = latest_revision.text
- )
- # send tags updated singal
- tags_updated.send(sender=question.__class__, question=question)
-
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- form = RetagQuestionForm(question)
- return render_to_response('question_retag.html', {
- 'question': question,
- 'form' : form,
- 'tags' : _get_tags_cache_json(),
- }, context_instance=RequestContext(request))
-
-def _edit_question(request, question):
- latest_revision = question.get_latest_revision()
- revision_form = None
- if request.method == 'POST':
- if 'select_revision' in request.POST:
- # user has changed revistion number
- revision_form = RevisionForm(question, latest_revision, request.POST)
- if revision_form.is_valid():
- # Replace with those from the selected revision
- form = EditQuestionForm(question,
- QuestionRevision.objects.get(question=question,
- revision=revision_form.cleaned_data['revision']))
- else:
- form = EditQuestionForm(question, latest_revision, request.POST)
- else:
- # Always check modifications against the latest revision
- form = EditQuestionForm(question, latest_revision, request.POST)
- if form.is_valid():
- html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
- if form.has_changed():
- edited_at = datetime.datetime.now()
- tags_changed = (latest_revision.tagnames !=
- form.cleaned_data['tags'])
- tags_updated = False
- # Update the Question itself
- updated_fields = {
- 'title': form.cleaned_data['title'],
- 'last_edited_at': edited_at,
- 'last_edited_by': request.user,
- 'last_activity_at': edited_at,
- 'last_activity_by': request.user,
- 'tagnames': form.cleaned_data['tags'],
- 'summary': strip_tags(html)[:120],
- 'html': html,
- }
-
- # only save when it's checked
- # because wiki doesn't allow to be edited if last version has been enabled already
- # and we make sure this in forms.
- if ('wiki' in form.cleaned_data and
- form.cleaned_data['wiki']):
- updated_fields['wiki'] = True
- updated_fields['wikified_at'] = edited_at
-
- Question.objects.filter(
- id=question.id).update(**updated_fields)
- # Update the Question's tag associations
- if tags_changed:
- tags_updated = Question.objects.update_tags(
- question, form.cleaned_data['tags'], request.user)
- # Create a new revision
- revision = QuestionRevision(
- question = question,
- title = form.cleaned_data['title'],
- author = request.user,
- revised_at = edited_at,
- tagnames = form.cleaned_data['tags'],
- text = form.cleaned_data['text'],
- )
- if form.cleaned_data['summary']:
- revision.summary = form.cleaned_data['summary']
- else:
- revision.summary = 'No.%s Revision' % latest_revision.revision
- revision.save()
-
- return HttpResponseRedirect(question.get_absolute_url())
- else:
-
- revision_form = RevisionForm(question, latest_revision)
- form = EditQuestionForm(question, latest_revision)
- return render_to_response('question_edit.html', {
- 'question': question,
- 'revision_form': revision_form,
- 'form' : form,
- 'tags' : _get_tags_cache_json()
- }, context_instance=RequestContext(request))
-
-
-@login_required
-def edit_answer(request, id):
- answer = get_object_or_404(Answer, id=id)
- if answer.deleted and not can_view_deleted_post(request.user, answer):
- raise Http404
- elif not can_edit_post(request.user, answer):
- raise Http404
- else:
- latest_revision = answer.get_latest_revision()
- if request.method == "POST":
- if 'select_revision' in request.POST:
- # user has changed revistion number
- revision_form = RevisionForm(answer, latest_revision, request.POST)
- if revision_form.is_valid():
- # Replace with those from the selected revision
- form = EditAnswerForm(answer,
- AnswerRevision.objects.get(answer=answer,
- revision=revision_form.cleaned_data['revision']))
- else:
- form = EditAnswerForm(answer, latest_revision, request.POST)
- else:
- form = EditAnswerForm(answer, latest_revision, request.POST)
- if form.is_valid():
- html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
- if form.has_changed():
- edited_at = datetime.datetime.now()
- updated_fields = {
- 'last_edited_at': edited_at,
- 'last_edited_by': request.user,
- 'html': html,
- }
- Answer.objects.filter(id=answer.id).update(**updated_fields)
-
- revision = AnswerRevision(
- answer=answer,
- author=request.user,
- revised_at=edited_at,
- text=form.cleaned_data['text']
- )
-
- if form.cleaned_data['summary']:
- revision.summary = form.cleaned_data['summary']
- else:
- revision.summary = 'No.%s Revision' % latest_revision.revision
- revision.save()
-
- answer.question.last_activity_at = edited_at
- answer.question.last_activity_by = request.user
- answer.question.save()
-
- return HttpResponseRedirect(answer.get_absolute_url())
- else:
- revision_form = RevisionForm(answer, latest_revision)
- form = EditAnswerForm(answer, latest_revision)
- return render_to_response('answer_edit.html', {
- 'answer': answer,
- 'revision_form': revision_form,
- 'form': form,
- }, context_instance=RequestContext(request))
-
-QUESTION_REVISION_TEMPLATE = ('<h1>%(title)s</h1>\n'
- '<div class="text">%(html)s</div>\n'
- '<div class="tags">%(tags)s</div>')
-def question_revisions(request, id):
- post = get_object_or_404(Question, id=id)
- revisions = list(post.revisions.all())
- revisions.reverse()
- for i, revision in enumerate(revisions):
- revision.html = QUESTION_REVISION_TEMPLATE % {
- 'title': revision.title,
- 'html': sanitize_html(markdowner.convert(revision.text)),
- 'tags': ' '.join(['<a class="post-tag">%s</a>' % tag
- for tag in revision.tagnames.split(' ')]),
- }
- if i > 0:
- revisions[i].diff = htmldiff(revisions[i-1].html, revision.html)
- else:
- revisions[i].diff = QUESTION_REVISION_TEMPLATE % {
- 'title': revisions[0].title,
- 'html': sanitize_html(markdowner.convert(revisions[0].text)),
- 'tags': ' '.join(['<a class="post-tag">%s</a>' % tag
- for tag in revisions[0].tagnames.split(' ')]),
- }
- revisions[i].summary = _('initial version')
- return render_to_response('revisions_question.html', {
- 'post': post,
- 'revisions': revisions,
- }, context_instance=RequestContext(request))
-
-ANSWER_REVISION_TEMPLATE = ('<div class="text">%(html)s</div>')
-def answer_revisions(request, id):
- post = get_object_or_404(Answer, id=id)
- revisions = list(post.revisions.all())
- revisions.reverse()
- for i, revision in enumerate(revisions):
- revision.html = ANSWER_REVISION_TEMPLATE % {
- 'html': sanitize_html(markdowner.convert(revision.text))
- }
- if i > 0:
- revisions[i].diff = htmldiff(revisions[i-1].html, revision.html)
- else:
- revisions[i].diff = revisions[i].text
- revisions[i].summary = _('initial version')
- return render_to_response('revisions_answer.html', {
- 'post': post,
- 'revisions': revisions,
- }, context_instance=RequestContext(request))
-
-def answer(request, id):
- question = get_object_or_404(Question, id=id)
- if request.method == "POST":
- form = AnswerForm(question, request.user, request.POST)
- if form.is_valid():
- wiki = form.cleaned_data['wiki']
- text = form.cleaned_data['text']
- update_time = datetime.datetime.now()
-
- if request.user.is_authenticated():
- create_new_answer(
- question=question,
- author=request.user,
- added_at=update_time,
- wiki=wiki,
- text=text,
- email_notify=form.cleaned_data['email_notify']
- )
- else:
- request.session.flush()
- html = sanitize_html(markdowner.convert(text))
- summary = strip_tags(html)[:120]
- anon = AnonymousAnswer(
- question=question,
- wiki=wiki,
- text=text,
- summary=summary,
- session_key=request.session.session_key,
- ip_addr=request.META['REMOTE_ADDR'],
- )
- anon.save()
- return HttpResponseRedirect(reverse('user_signin_new_answer'))
-
- return HttpResponseRedirect(question.get_absolute_url())
-
-def tags(request):
- stag = ""
- is_paginated = True
- sortby = request.GET.get('sort', 'used')
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
-
- if request.method == "GET":
- stag = request.GET.get("q", "").strip()
- if stag != '':
- objects_list = Paginator(Tag.objects.filter(deleted=False).exclude(used_count=0).extra(where=['name like %s'], params=['%' + stag + '%']), DEFAULT_PAGE_SIZE)
- else:
- if sortby == "name":
- objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE)
- else:
- objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE)
-
- try:
- tags = objects_list.page(page)
- except (EmptyPage, InvalidPage):
- tags = objects_list.page(objects_list.num_pages)
-
- return render_to_response('tags.html', {
- "tags" : tags,
- "stag" : stag,
- "tab_id" : sortby,
- "keywords" : stag,
- "context" : {
- 'is_paginated' : is_paginated,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': tags.has_previous(),
- 'has_next': tags.has_next(),
- 'previous': tags.previous_page_number(),
- 'next': tags.next_page_number(),
- 'base_url' : reverse('tags') + '?sort=%s&' % sortby
- }
- }, context_instance=RequestContext(request))
-
-def tag(request, tag):
- return questions(request, tagname=tag)
-
-def vote(request, id):
- """
- vote_type:
- acceptAnswer : 0,
- questionUpVote : 1,
- questionDownVote : 2,
- favorite : 4,
- answerUpVote: 5,
- answerDownVote:6,
- offensiveQuestion : 7,
- offensiveAnswer:8,
- removeQuestion: 9,
- removeAnswer:10
- questionSubscribeUpdates:11
-
- accept answer code:
- response_data['allowed'] = -1, Accept his own answer 0, no allowed - Anonymous 1, Allowed - by default
- response_data['success'] = 0, failed 1, Success - by default
- response_data['status'] = 0, By default 1, Answer has been accepted already(Cancel)
-
- vote code:
- allowed = -3, Don't have enough votes left
- -2, Don't have enough reputation score
- -1, Vote his own post
- 0, no allowed - Anonymous
- 1, Allowed - by default
- status = 0, By default
- 1, Cancel
- 2, Vote is too old to be canceled
-
- offensive code:
- allowed = -3, Don't have enough flags left
- -2, Don't have enough reputation score to do this
- 0, not allowed
- 1, allowed
- status = 0, by default
- 1, can't do it again
- """
- response_data = {
- "allowed": 1,
- "success": 1,
- "status" : 0,
- "count" : 0,
- "message" : ''
- }
-
- def can_vote(vote_score, user):
- if vote_score == 1:
- return can_vote_up(request.user)
- else:
- return can_vote_down(request.user)
-
- try:
- if not request.user.is_authenticated():
- response_data['allowed'] = 0
- response_data['success'] = 0
-
- elif request.is_ajax():
- question = get_object_or_404(Question, id=id)
- vote_type = request.POST.get('type')
-
- #accept answer
- if vote_type == '0':
- answer_id = request.POST.get('postId')
- answer = get_object_or_404(Answer, id=answer_id)
- # make sure question author is current user
- if question.author == request.user:
- # answer user who is also question author is not allow to accept answer
- if answer.author == question.author:
- response_data['success'] = 0
- response_data['allowed'] = -1
- # check if answer has been accepted already
- elif answer.accepted:
- onAnswerAcceptCanceled(answer, request.user)
- response_data['status'] = 1
- else:
- # set other answers in this question not accepted first
- for answer_of_question in Answer.objects.get_answers_from_question(question, request.user):
- if answer_of_question != answer and answer_of_question.accepted:
- onAnswerAcceptCanceled(answer_of_question, request.user)
-
- #make sure retrieve data again after above author changes, they may have related data
- answer = get_object_or_404(Answer, id=answer_id)
- onAnswerAccept(answer, request.user)
- else:
- response_data['allowed'] = 0
- response_data['success'] = 0
- # favorite
- elif vote_type == '4':
- has_favorited = False
- fav_questions = FavoriteQuestion.objects.filter(question=question)
- # if the same question has been favorited before, then delete it
- if fav_questions is not None:
- for item in fav_questions:
- if item.user == request.user:
- item.delete()
- response_data['status'] = 1
- response_data['count'] = len(fav_questions) - 1
- if response_data['count'] < 0:
- response_data['count'] = 0
- has_favorited = True
- # if above deletion has not been executed, just insert a new favorite question
- if not has_favorited:
- new_item = FavoriteQuestion(question=question, user=request.user)
- new_item.save()
- response_data['count'] = FavoriteQuestion.objects.filter(question=question).count()
- Question.objects.update_favorite_count(question)
-
- elif vote_type in ['1', '2', '5', '6']:
- post_id = id
- post = question
- vote_score = 1
- if vote_type in ['5', '6']:
- answer_id = request.POST.get('postId')
- answer = get_object_or_404(Answer, id=answer_id)
- post_id = answer_id
- post = answer
- if vote_type in ['2', '6']:
- vote_score = -1
-
- if post.author == request.user:
- response_data['allowed'] = -1
- elif not can_vote(vote_score, request.user):
- response_data['allowed'] = -2
- elif post.votes.filter(user=request.user).count() > 0:
- vote = post.votes.filter(user=request.user)[0]
- # unvote should be less than certain time
- if (datetime.datetime.now().day - vote.voted_at.day) >= VOTE_RULES['scope_deny_unvote_days']:
- response_data['status'] = 2
- else:
- voted = vote.vote
- if voted > 0:
- # cancel upvote
- onUpVotedCanceled(vote, post, request.user)
-
- else:
- # cancel downvote
- onDownVotedCanceled(vote, post, request.user)
-
- response_data['status'] = 1
- response_data['count'] = post.score
- elif Vote.objects.get_votes_count_today_from_user(request.user) >= VOTE_RULES['scope_votes_per_user_per_day']:
- response_data['allowed'] = -3
- else:
- vote = Vote(user=request.user, content_object=post, vote=vote_score, voted_at=datetime.datetime.now())
- if vote_score > 0:
- # upvote
- onUpVoted(vote, post, request.user)
- else:
- # downvote
- onDownVoted(vote, post, request.user)
-
- votes_left = VOTE_RULES['scope_votes_per_user_per_day'] - Vote.objects.get_votes_count_today_from_user(request.user)
- if votes_left <= VOTE_RULES['scope_warn_votes_left']:
- response_data['message'] = u'%s votes left' % votes_left
- response_data['count'] = post.score
- elif vote_type in ['7', '8']:
- post = question
- post_id = id
- if vote_type == '8':
- post_id = request.POST.get('postId')
- post = get_object_or_404(Answer, id=post_id)
-
- if FlaggedItem.objects.get_flagged_items_count_today(request.user) >= VOTE_RULES['scope_flags_per_user_per_day']:
- response_data['allowed'] = -3
- elif not can_flag_offensive(request.user):
- response_data['allowed'] = -2
- elif post.flagged_items.filter(user=request.user).count() > 0:
- response_data['status'] = 1
- else:
- item = FlaggedItem(user=request.user, content_object=post, flagged_at=datetime.datetime.now())
- onFlaggedItem(item, post, request.user)
- response_data['count'] = post.offensive_flag_count
- # send signal when question or answer be marked offensive
- mark_offensive.send(sender=post.__class__, instance=post, mark_by=request.user)
- elif vote_type in ['9', '10']:
- post = question
- post_id = id
- if vote_type == '10':
- post_id = request.POST.get('postId')
- post = get_object_or_404(Answer, id=post_id)
-
- if not can_delete_post(request.user, post):
- response_data['allowed'] = -2
- elif post.deleted == True:
- logging.debug('debug restoring post in view')
- onDeleteCanceled(post, request.user)
- response_data['status'] = 1
- else:
- onDeleted(post, request.user)
- delete_post_or_answer.send(sender=post.__class__, instance=post, delete_by=request.user)
- elif vote_type == '11':#subscribe q updates
- user = request.user
- if user.is_authenticated():
- if user not in question.followed_by.all():
- question.followed_by.add(user)
- if settings.EMAIL_VALIDATION == 'on' and user.email_isvalid == False:
- response_data['message'] = \
- _('subscription saved, %(email)s needs validation, see %(details_url)s') \
- % {'email':user.email,'details_url':reverse('faq') + '#validate'}
- feed_setting = EmailFeedSetting.objects.get(subscriber=user,feed_type='q_sel')
- if feed_setting.frequency == 'n':
- feed_setting.frequency = 'd'
- feed_setting.save()
- if 'message' in response_data:
- response_data['message'] += '<br/>'
- response_data['message'] = _('email update frequency has been set to daily')
- #response_data['status'] = 1
- #responst_data['allowed'] = 1
- else:
- pass
- #response_data['status'] = 0
- #response_data['allowed'] = 0
- elif vote_type == '12':#unsubscribe q updates
- user = request.user
- if user.is_authenticated():
- if user in question.followed_by.all():
- question.followed_by.remove(user)
- else:
- response_data['success'] = 0
- response_data['message'] = u'Request mode is not supported. Please try again.'
-
- data = simplejson.dumps(response_data)
-
- except Exception, e:
- response_data['message'] = str(e)
- data = simplejson.dumps(response_data)
- return HttpResponse(data, mimetype="application/json")
-
-@ajax_login_required
-def mark_tag(request, tag=None, **kwargs):
- action = kwargs['action']
- ts = MarkedTag.objects.filter(user=request.user, tag__name=tag)
- if action == 'remove':
- logging.debug('deleting tag %s' % tag)
- ts.delete()
- else:
- reason = kwargs['reason']
- if len(ts) == 0:
- try:
- t = Tag.objects.get(name=tag)
- mt = MarkedTag(user=request.user, reason=reason, tag=t)
- mt.save()
- except:
- pass
- else:
- ts.update(reason=reason)
- return HttpResponse(simplejson.dumps(''), mimetype="application/json")
-
-@ajax_login_required
-def ajax_toggle_ignored_questions(request):
- if request.user.hide_ignored_questions:
- new_hide_setting = False
- else:
- new_hide_setting = True
- request.user.hide_ignored_questions = new_hide_setting
- request.user.save()
-
-@ajax_method
-def ajax_command(request):
- if 'command' not in request.POST:
- return HttpResponseForbidden(mimetype="application/json")
- if request.POST['command'] == 'toggle-ignored-questions':
- return ajax_toggle_ignored_questions(request)
-
-def users(request):
- is_paginated = True
- sortby = request.GET.get('sort', 'reputation')
- suser = request.REQUEST.get('q', "")
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
-
- if suser == "":
- if sortby == "newest":
- objects_list = Paginator(User.objects.all().order_by('-date_joined'), USERS_PAGE_SIZE)
- elif sortby == "last":
- objects_list = Paginator(User.objects.all().order_by('date_joined'), USERS_PAGE_SIZE)
- elif sortby == "user":
- objects_list = Paginator(User.objects.all().order_by('username'), USERS_PAGE_SIZE)
- # default
- else:
- objects_list = Paginator(User.objects.all().order_by('-reputation'), USERS_PAGE_SIZE)
- base_url = reverse('users') + '?sort=%s&' % sortby
- else:
- sortby = "reputation"
- objects_list = Paginator(User.objects.extra(where=['username like %s'], params=['%' + suser + '%']).order_by('-reputation'), USERS_PAGE_SIZE)
- base_url = reverse('users') + '?name=%s&sort=%s&' % (suser, sortby)
-
- try:
- users = objects_list.page(page)
- except (EmptyPage, InvalidPage):
- users = objects_list.page(objects_list.num_pages)
-
- return render_to_response('users.html', {
- "users" : users,
- "suser" : suser,
- "keywords" : suser,
- "tab_id" : sortby,
- "context" : {
- 'is_paginated' : is_paginated,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': users.has_previous(),
- 'has_next': users.has_next(),
- 'previous': users.previous_page_number(),
- 'next': users.next_page_number(),
- 'base_url' : base_url
- }
-
- }, context_instance=RequestContext(request))
-
-def user(request, id):
- sort = request.GET.get('sort', 'stats')
- user_view = dict((v.id, v) for v in USER_TEMPLATE_VIEWS).get(sort, USER_TEMPLATE_VIEWS[0])
- from forum import views
- func = getattr(views, user_view.view_name)
- return func(request, id, user_view)
-
-@login_required
-def moderate_user(request, id):
- """ajax handler of user moderation
- """
- if not auth.can_moderate_users(request.user) or request.method != 'POST':
- raise Http404
- if not request.is_ajax():
- return HttpResponseForbidden(mimetype="application/json")
-
- user = get_object_or_404(User, id=id)
- form = ModerateUserForm(request.POST, instance=user)
-
- if form.is_valid():
- form.save()
- logging.debug('data saved')
- response = HttpResponse(simplejson.dumps(''), mimetype="application/json")
- else:
- response = HttpResponseForbidden(mimetype="application/json")
- return response
-
-@login_required
-def edit_user(request, id):
- user = get_object_or_404(User, id=id)
- if request.user != user:
- raise Http404
- if request.method == "POST":
- form = EditUserForm(user, request.POST)
- 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'])
- user.real_name = sanitize_html(form.cleaned_data['realname'])
- user.website = sanitize_html(form.cleaned_data['website'])
- user.location = sanitize_html(form.cleaned_data['city'])
- user.date_of_birth = sanitize_html(form.cleaned_data['birthday'])
- if len(user.date_of_birth) == 0:
- user.date_of_birth = '1900-01-01'
- user.about = sanitize_html(form.cleaned_data['about'])
-
- user.save()
- # send user updated singal if full fields have been updated
- if user.email and user.real_name and user.website and user.location and \
- user.date_of_birth and user.about:
- user_updated.send(sender=user.__class__, instance=user, updated_by=user)
- return HttpResponseRedirect(user.get_profile_url())
- else:
- form = EditUserForm(user)
- return render_to_response('user_edit.html', {
- 'form' : form,
- 'gravatar_faq_url' : reverse('faq') + '#gravatar',
- }, context_instance=RequestContext(request))
-
-def user_stats(request, user_id, user_view):
- user = get_object_or_404(User, id=user_id)
- questions = Question.objects.extra(
- select={
- 'vote_count' : 'question.score',
- 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id',
- 'la_user_id' : 'auth_user.id',
- 'la_username' : 'auth_user.username',
- 'la_user_gold' : 'auth_user.gold',
- 'la_user_silver' : 'auth_user.silver',
- 'la_user_bronze' : 'auth_user.bronze',
- 'la_user_reputation' : 'auth_user.reputation'
- },
- select_params=[user_id],
- tables=['question', 'auth_user'],
- where=['question.deleted=False AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'],
- params=[user_id],
- order_by=['-vote_count', '-last_activity_at']
- ).values('vote_count',
- 'favorited_myself',
- 'id',
- 'title',
- 'author_id',
- 'added_at',
- 'answer_accepted',
- 'answer_count',
- 'comment_count',
- 'view_count',
- 'favourite_count',
- 'summary',
- 'tagnames',
- 'vote_up_count',
- 'vote_down_count',
- 'last_activity_at',
- 'la_user_id',
- 'la_username',
- 'la_user_gold',
- 'la_user_silver',
- 'la_user_bronze',
- 'la_user_reputation')[:100]
-
- answered_questions = Question.objects.extra(
- select={
- 'vote_up_count' : 'answer.vote_up_count',
- 'vote_down_count' : 'answer.vote_down_count',
- 'answer_id' : 'answer.id',
- 'accepted' : 'answer.accepted',
- 'vote_count' : 'answer.score',
- 'comment_count' : 'answer.comment_count'
- },
- tables=['question', 'answer'],
- where=['answer.deleted=False AND question.deleted=False AND answer.author_id=%s AND answer.question_id=question.id'],
- params=[user_id],
- order_by=['-vote_count', '-answer_id'],
- select_params=[user_id]
- ).distinct().values('comment_count',
- 'id',
- 'answer_id',
- 'title',
- 'author_id',
- 'accepted',
- 'vote_count',
- 'answer_count',
- 'vote_up_count',
- 'vote_down_count')[:100]
-
- up_votes = Vote.objects.get_up_vote_count_from_user(user)
- down_votes = Vote.objects.get_down_vote_count_from_user(user)
- votes_today = Vote.objects.get_votes_count_today_from_user(user)
- votes_total = VOTE_RULES['scope_votes_per_user_per_day']
-
- question_id_set = set(map(lambda v: v['id'], list(questions))) \
- | set(map(lambda v: v['id'], list(answered_questions)))
-
- user_tags = Tag.objects.filter(questions__id__in = question_id_set)
- try:
- from django.db.models import Count
- awards = Award.objects.extra(
- select={'id': 'badge.id',
- 'name':'badge.name',
- 'description': 'badge.description',
- 'type': 'badge.type'},
- tables=['award', 'badge'],
- order_by=['-awarded_at'],
- where=['user_id=%s AND badge_id=badge.id'],
- params=[user.id]
- ).values('id', 'name', 'description', 'type')
- total_awards = awards.count()
- awards = awards.annotate(count = Count('badge__id'))
- user_tags = user_tags.annotate(user_tag_usage_count=Count('name'))
-
- except ImportError:
- awards = Award.objects.extra(
- select={'id': 'badge.id',
- 'count': 'count(badge_id)',
- 'name':'badge.name',
- 'description': 'badge.description',
- 'type': 'badge.type'},
- tables=['award', 'badge'],
- order_by=['-awarded_at'],
- where=['user_id=%s AND badge_id=badge.id'],
- params=[user.id]
- ).values('id', 'count', 'name', 'description', 'type')
- total_awards = awards.count()
- awards.query.group_by = ['badge_id']
-
- user_tags = user_tags.extra(
- select={'user_tag_usage_count': 'COUNT(1)',},
- order_by=['-user_tag_usage_count'],
- )
- user_tags.query.group_by = ['name']
-
- if auth.can_moderate_users(request.user):
- moderate_user_form = ModerateUserForm(instance=user)
- else:
- moderate_user_form = None
-
- return render_to_response(user_view.template_file,{
- 'moderate_user_form': moderate_user_form,
- "tab_name" : user_view.id,
- "tab_description" : user_view.tab_description,
- "page_title" : user_view.page_title,
- "view_user" : user,
- "questions" : questions,
- "answered_questions" : answered_questions,
- "up_votes" : up_votes,
- "down_votes" : down_votes,
- "total_votes": up_votes + down_votes,
- "votes_today_left": votes_total-votes_today,
- "votes_total_per_day": votes_total,
- "user_tags" : user_tags[:50],
- "tags" : tags,
- "awards": awards,
- "total_awards" : total_awards,
- }, context_instance=RequestContext(request))
-
-def user_recent(request, user_id, user_view):
- user = get_object_or_404(User, id=user_id)
- def get_type_name(type_id):
- for item in TYPE_ACTIVITY:
- if type_id in item:
- return item[1]
-
- class Event:
- def __init__(self, time, type, title, summary, answer_id, question_id):
- self.time = time
- self.type = get_type_name(type)
- self.type_id = type
- self.title = title
- self.summary = summary
- slug_title = slugify(title)
- self.title_link = reverse('question', kwargs={'id':question_id}) + u'%s' % slug_title
- if int(answer_id) > 0:
- self.title_link += '#%s' % answer_id
-
- class AwardEvent:
- def __init__(self, time, type, id):
- self.time = time
- self.type = get_type_name(type)
- self.type_id = type
- self.badge = get_object_or_404(Badge, id=id)
-
- activities = []
- # ask questions
- questions = Activity.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'active_at' : 'activity.active_at',
- 'activity_type' : 'activity.activity_type'
- },
- tables=['activity', 'question'],
- where=['activity.content_type_id = %s AND activity.object_id = ' +
- 'question.id AND question.deleted=False AND activity.user_id = %s AND activity.activity_type = %s'],
- params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION],
- order_by=['-activity.active_at']
- ).values(
- 'title',
- 'question_id',
- 'active_at',
- 'activity_type'
- )
- if len(questions) > 0:
- questions = [(Event(q['active_at'], q['activity_type'], q['title'], '', '0', \
- q['question_id'])) for q in questions]
- activities.extend(questions)
-
- # answers
- answers = Activity.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 'answer.id',
- 'active_at' : 'activity.active_at',
- 'activity_type' : 'activity.activity_type'
- },
- tables=['activity', 'answer', 'question'],
- where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' +
- 'answer.question_id=question.id AND answer.deleted=False AND activity.user_id=%s AND '+
- 'activity.activity_type=%s AND question.deleted=False'],
- params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER],
- order_by=['-activity.active_at']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'active_at',
- 'activity_type'
- )
- if len(answers) > 0:
- answers = [(Event(q['active_at'], q['activity_type'], q['title'], '', q['answer_id'], \
- q['question_id'])) for q in answers]
- activities.extend(answers)
-
- # question comments
- comments = Activity.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'comment.object_id',
- 'added_at' : 'comment.added_at',
- 'activity_type' : 'activity.activity_type'
- },
- tables=['activity', 'question', 'comment'],
-
- where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+
- 'activity.user_id = comment.user_id AND comment.object_id=question.id AND '+
- 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s AND ' +
- 'question.deleted=False'],
- params=[comment_type_id, question_type_id, user_id, TYPE_ACTIVITY_COMMENT_QUESTION],
- order_by=['-comment.added_at']
- ).values(
- 'title',
- 'question_id',
- 'added_at',
- 'activity_type'
- )
-
- if len(comments) > 0:
- comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \
- q['question_id'])) for q in comments]
- activities.extend(comments)
-
- # answer comments
- comments = Activity.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 'answer.id',
- 'added_at' : 'comment.added_at',
- 'activity_type' : 'activity.activity_type'
- },
- tables=['activity', 'question', 'answer', 'comment'],
-
- where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+
- 'activity.user_id = comment.user_id AND comment.object_id=answer.id AND '+
- 'comment.content_type_id=%s AND question.id = answer.question_id AND '+
- 'activity.user_id = %s AND activity.activity_type=%s AND '+
- 'answer.deleted=False AND question.deleted=False'],
- params=[comment_type_id, answer_type_id, user_id, TYPE_ACTIVITY_COMMENT_ANSWER],
- order_by=['-comment.added_at']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'added_at',
- 'activity_type'
- )
-
- if len(comments) > 0:
- comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', q['answer_id'], \
- q['question_id'])) for q in comments]
- activities.extend(comments)
-
- # question revisions
- revisions = Activity.objects.extra(
- select={
- 'title' : 'question_revision.title',
- 'question_id' : 'question_revision.question_id',
- 'added_at' : 'activity.active_at',
- 'activity_type' : 'activity.activity_type',
- 'summary' : 'question_revision.summary'
- },
- tables=['activity', 'question_revision', 'question'],
- where=['activity.content_type_id = %s AND activity.object_id = question_revision.id AND '+
- 'question_revision.id=question.id AND question.deleted=False AND '+
- 'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+
- 'activity.activity_type=%s'],
- params=[question_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_QUESTION],
- order_by=['-activity.active_at']
- ).values(
- 'title',
- 'question_id',
- 'added_at',
- 'activity_type',
- 'summary'
- )
-
- if len(revisions) > 0:
- revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], '0', \
- q['question_id'])) for q in revisions]
- activities.extend(revisions)
-
- # answer revisions
- revisions = Activity.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 'answer.id',
- 'added_at' : 'activity.active_at',
- 'activity_type' : 'activity.activity_type',
- 'summary' : 'answer_revision.summary'
- },
- tables=['activity', 'answer_revision', 'question', 'answer'],
-
- where=['activity.content_type_id = %s AND activity.object_id = answer_revision.id AND '+
- 'activity.user_id = answer_revision.author_id AND activity.user_id = %s AND '+
- 'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+
- 'question.deleted=False AND answer.deleted=False AND '+
- 'activity.activity_type=%s'],
- params=[answer_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_ANSWER],
- order_by=['-activity.active_at']
- ).values(
- 'title',
- 'question_id',
- 'added_at',
- 'answer_id',
- 'activity_type',
- 'summary'
- )
-
- if len(revisions) > 0:
- revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], \
- q['answer_id'], q['question_id'])) for q in revisions]
- activities.extend(revisions)
-
- # accepted answers
- accept_answers = Activity.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'added_at' : 'activity.active_at',
- 'activity_type' : 'activity.activity_type',
- },
- tables=['activity', 'answer', 'question'],
- where=['activity.content_type_id = %s AND activity.object_id = answer.id AND '+
- 'activity.user_id = question.author_id AND activity.user_id = %s AND '+
- 'answer.deleted=False AND question.deleted=False AND '+
- 'answer.question_id=question.id AND activity.activity_type=%s'],
- params=[answer_type_id, user_id, TYPE_ACTIVITY_MARK_ANSWER],
- order_by=['-activity.active_at']
- ).values(
- 'title',
- 'question_id',
- 'added_at',
- 'activity_type',
- )
- if len(accept_answers) > 0:
- accept_answers = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \
- q['question_id'])) for q in accept_answers]
- activities.extend(accept_answers)
- #award history
- awards = Activity.objects.extra(
- select={
- 'badge_id' : 'badge.id',
- 'awarded_at': 'award.awarded_at',
- 'activity_type' : 'activity.activity_type'
- },
- tables=['activity', 'award', 'badge'],
- where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+
- 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'],
- params=[user_id, TYPE_ACTIVITY_PRIZE],
- order_by=['-activity.active_at']
- ).values(
- 'badge_id',
- 'awarded_at',
- 'activity_type'
- )
- if len(awards) > 0:
- awards = [(AwardEvent(q['awarded_at'], q['activity_type'], q['badge_id'])) for q in awards]
- activities.extend(awards)
-
- activities.sort(lambda x,y: cmp(y.time, x.time))
-
- return render_to_response(user_view.template_file,{
- "tab_name" : user_view.id,
- "tab_description" : user_view.tab_description,
- "page_title" : user_view.page_title,
- "view_user" : user,
- "activities" : activities[:user_view.data_size]
- }, context_instance=RequestContext(request))
-
-def user_responses(request, user_id, user_view):
- """
- We list answers for question, comments, and answer accepted by others for this user.
- """
- class Response:
- def __init__(self, type, title, question_id, answer_id, time, username, user_id, content):
- self.type = type
- self.title = title
- self.titlelink = reverse('question', args=[question_id]) + u'%s#%s' % (slugify(title), answer_id)
- self.time = time
- self.userlink = reverse('users') + u'%s/%s/' % (user_id, username)
- self.username = username
- self.content = u'%s ...' % strip_tags(content)[:300]
-
- def __unicode__(self):
- return u'%s %s' % (self.type, self.titlelink)
-
- user = get_object_or_404(User, id=user_id)
- responses = []
- answers = Answer.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 'answer.id',
- 'added_at' : 'answer.added_at',
- 'html' : 'answer.html',
- 'username' : 'auth_user.username',
- 'user_id' : 'auth_user.id'
- },
- select_params=[user_id],
- tables=['answer', 'question', 'auth_user'],
- where=['answer.question_id = question.id AND answer.deleted=False AND question.deleted=False AND '+
- 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'],
- params=[user_id, user_id],
- order_by=['-answer.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'added_at',
- 'html',
- 'username',
- 'user_id'
- )
- if len(answers) > 0:
- answers = [(Response(TYPE_RESPONSE['QUESTION_ANSWERED'], a['title'], a['question_id'],
- a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
- responses.extend(answers)
-
-
- # question comments
- comments = Comment.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'comment.object_id',
- 'added_at' : 'comment.added_at',
- 'comment' : 'comment.comment',
- 'username' : 'auth_user.username',
- 'user_id' : 'auth_user.id'
- },
- tables=['question', 'auth_user', 'comment'],
- where=['question.deleted=False AND question.author_id = %s AND comment.object_id=question.id AND '+
- 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'],
- params=[user_id, question_type_id, user_id],
- order_by=['-comment.added_at']
- ).values(
- 'title',
- 'question_id',
- 'added_at',
- 'comment',
- 'username',
- 'user_id'
- )
-
- if len(comments) > 0:
- comments = [(Response(TYPE_RESPONSE['QUESTION_COMMENTED'], c['title'], c['question_id'],
- '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
- responses.extend(comments)
-
- # answer comments
- comments = Comment.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 'answer.id',
- 'added_at' : 'comment.added_at',
- 'comment' : 'comment.comment',
- 'username' : 'auth_user.username',
- 'user_id' : 'auth_user.id'
- },
- tables=['answer', 'auth_user', 'comment', 'question'],
- where=['answer.deleted=False AND answer.author_id = %s AND comment.object_id=answer.id AND '+
- 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id '+
- 'AND question.id = answer.question_id'],
- params=[user_id, answer_type_id, user_id],
- order_by=['-comment.added_at']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'added_at',
- 'comment',
- 'username',
- 'user_id'
- )
-
- if len(comments) > 0:
- comments = [(Response(TYPE_RESPONSE['ANSWER_COMMENTED'], c['title'], c['question_id'],
- c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
- responses.extend(comments)
-
- # answer has been accepted
- answers = Answer.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 'answer.id',
- 'added_at' : 'answer.accepted_at',
- 'html' : 'answer.html',
- 'username' : 'auth_user.username',
- 'user_id' : 'auth_user.id'
- },
- select_params=[user_id],
- tables=['answer', 'question', 'auth_user'],
- where=['answer.question_id = question.id AND answer.deleted=False AND question.deleted=False AND '+
- 'answer.author_id = %s AND answer.accepted=True AND question.author_id=auth_user.id'],
- params=[user_id],
- order_by=['-answer.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'added_at',
- 'html',
- 'username',
- 'user_id'
- )
- if len(answers) > 0:
- answers = [(Response(TYPE_RESPONSE['ANSWER_ACCEPTED'], a['title'], a['question_id'],
- a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
- responses.extend(answers)
-
- # sort posts by time
- responses.sort(lambda x,y: cmp(y.time, x.time))
-
- return render_to_response(user_view.template_file,{
- "tab_name" : user_view.id,
- "tab_description" : user_view.tab_description,
- "page_title" : user_view.page_title,
- "view_user" : user,
- "responses" : responses[:user_view.data_size],
-
- }, context_instance=RequestContext(request))
-
-def user_votes(request, user_id, user_view):
- user = get_object_or_404(User, id=user_id)
- if not can_view_user_votes(request.user, user):
- raise Http404
- votes = []
- question_votes = Vote.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 0,
- 'voted_at' : 'vote.voted_at',
- 'vote' : 'vote',
- },
- select_params=[user_id],
- tables=['vote', 'question', 'auth_user'],
- where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id '+
- 'AND vote.user_id=auth_user.id'],
- params=[question_type_id, user_id],
- order_by=['-vote.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'voted_at',
- 'vote',
- )
- if(len(question_votes) > 0):
- votes.extend(question_votes)
-
- answer_votes = Vote.objects.extra(
- select={
- 'title' : 'question.title',
- 'question_id' : 'question.id',
- 'answer_id' : 'answer.id',
- 'voted_at' : 'vote.voted_at',
- 'vote' : 'vote',
- },
- select_params=[user_id],
- tables=['vote', 'answer', 'question', 'auth_user'],
- where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id '+
- 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'],
- params=[answer_type_id, user_id],
- order_by=['-vote.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'voted_at',
- 'vote',
- )
- if(len(answer_votes) > 0):
- votes.extend(answer_votes)
- votes.sort(lambda x,y: cmp(y['voted_at'], x['voted_at']))
- return render_to_response(user_view.template_file,{
- "tab_name" : user_view.id,
- "tab_description" : user_view.tab_description,
- "page_title" : user_view.page_title,
- "view_user" : user,
- "votes" : votes[:user_view.data_size]
-
- }, context_instance=RequestContext(request))
-
-def user_reputation(request, user_id, user_view):
- user = get_object_or_404(User, id=user_id)
- try:
- from django.db.models import Sum
- reputation = Repute.objects.extra(
- select={'question_id':'question_id',
- 'title': 'question.title'},
- tables=['repute', 'question'],
- order_by=['-reputed_at'],
- where=['user_id=%s AND question_id=question.id'],
- params=[user.id]
- ).values('question_id', 'title', 'reputed_at', 'reputation')
- reputation = reputation.annotate(positive=Sum("positive"), negative=Sum("negative"))
- except ImportError:
- reputation = Repute.objects.extra(
- select={'positive':'sum(positive)', 'negative':'sum(negative)', 'question_id':'question_id',
- 'title': 'question.title'},
- tables=['repute', 'question'],
- order_by=['-reputed_at'],
- where=['user_id=%s AND question_id=question.id'],
- params=[user.id]
- ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation')
- reputation.query.group_by = ['question_id']
-
- rep_list = []
- for rep in Repute.objects.filter(user=user).order_by('reputed_at'):
- dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation)
- rep_list.append(dic)
- reps = ','.join(rep_list)
- reps = '[%s]' % reps
-
- return render_to_response(user_view.template_file, {
- "tab_name": user_view.id,
- "tab_description": user_view.tab_description,
- "page_title": user_view.page_title,
- "view_user": user,
- "reputation": reputation,
- "reps": reps
- }, context_instance=RequestContext(request))
-
-def user_favorites(request, user_id, user_view):
- user = get_object_or_404(User, id=user_id)
- questions = Question.objects.extra(
- select={
- 'vote_count' : 'question.vote_up_count + question.vote_down_count',
- 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s '+
- 'AND f.question_id = question.id',
- 'la_user_id' : 'auth_user.id',
- 'la_username' : 'auth_user.username',
- 'la_user_gold' : 'auth_user.gold',
- 'la_user_silver' : 'auth_user.silver',
- 'la_user_bronze' : 'auth_user.bronze',
- 'la_user_reputation' : 'auth_user.reputation'
- },
- select_params=[user_id],
- tables=['question', 'auth_user', 'favorite_question'],
- where=['question.deleted=True AND question.last_activity_by_id = auth_user.id '+
- 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'],
- params=[user_id],
- order_by=['-vote_count', '-question.id']
- ).values('vote_count',
- 'favorited_myself',
- 'id',
- 'title',
- 'author_id',
- 'added_at',
- 'answer_accepted',
- 'answer_count',
- 'comment_count',
- 'view_count',
- 'favourite_count',
- 'summary',
- 'tagnames',
- 'vote_up_count',
- 'vote_down_count',
- 'last_activity_at',
- 'la_user_id',
- 'la_username',
- 'la_user_gold',
- 'la_user_silver',
- 'la_user_bronze',
- 'la_user_reputation')
- return render_to_response(user_view.template_file,{
- "tab_name" : user_view.id,
- "tab_description" : user_view.tab_description,
- "page_title" : user_view.page_title,
- "questions" : questions[:user_view.data_size],
- "view_user" : user
- }, context_instance=RequestContext(request))
-
-def user_email_subscriptions(request, user_id, user_view):
- user = get_object_or_404(User, id=user_id)
- if request.method == 'POST':
- email_feeds_form = EditUserEmailFeedsForm(request.POST)
- tag_filter_form = TagFilterSelectionForm(request.POST, instance=user)
- if email_feeds_form.is_valid() and tag_filter_form.is_valid():
-
- action_status = None
- tag_filter_saved = tag_filter_form.save()
- if tag_filter_saved:
- action_status = _('changes saved')
- if 'save' in request.POST:
- feeds_saved = email_feeds_form.save(user)
- if feeds_saved:
- action_status = _('changes saved')
- elif 'stop_email' in request.POST:
- email_stopped = email_feeds_form.reset().save(user)
- initial_values = EditUserEmailFeedsForm.NO_EMAIL_INITIAL
- email_feeds_form = EditUserEmailFeedsForm(initial=initial_values)
- if email_stopped:
- action_status = _('email updates canceled')
- else:
- email_feeds_form = EditUserEmailFeedsForm()
- email_feeds_form.set_initial_values(user)
- tag_filter_form = TagFilterSelectionForm(instance=user)
- action_status = None
- return render_to_response(user_view.template_file,{
- 'tab_name':user_view.id,
- 'tab_description':user_view.tab_description,
- 'page_title':user_view.page_title,
- 'view_user':user,
- 'email_feeds_form':email_feeds_form,
- 'tag_filter_selection_form':tag_filter_form,
- 'action_status':action_status,
- }, context_instance=RequestContext(request))
-
-def question_comments(request, id):
- question = get_object_or_404(Question, id=id)
- user = request.user
- return __comments(request, question, 'question')
-
-def answer_comments(request, id):
- answer = get_object_or_404(Answer, id=id)
- user = request.user
- return __comments(request, answer, 'answer')
-
-def __comments(request, obj, type):
- # only support get comments by ajax now
- user = request.user
- if request.is_ajax():
- if request.method == "GET":
- response = __generate_comments_json(obj, type, user)
- elif request.method == "POST":
- if auth.can_add_comments(user,obj):
- comment_data = request.POST.get('comment')
- comment = Comment(content_object=obj, comment=comment_data, user=request.user)
- comment.save()
- obj.comment_count = obj.comment_count + 1
- obj.save()
- response = __generate_comments_json(obj, type, user)
- else:
- response = HttpResponseForbidden(mimetype="application/json")
- return response
-
-def __generate_comments_json(obj, type, user):
- comments = obj.comments.all().order_by('id')
- # {"Id":6,"PostId":38589,"CreationDate":"an hour ago","Text":"hello there!","UserDisplayName":"Jarrod Dixon","UserUrl":"/users/3/jarrod-dixon","DeleteUrl":null}
- json_comments = []
- from forum.templatetags.extra_tags import diff_date
- for comment in comments:
- comment_user = comment.user
- delete_url = ""
- if user != None and auth.can_delete_comment(user, comment):
- #/posts/392845/comments/219852/delete
- #todo translate this url
- delete_url = reverse(index) + type + "s/%s/comments/%s/delete/" % (obj.id, comment.id)
- json_comments.append({"id" : comment.id,
- "object_id" : obj.id,
- "comment_age" : diff_date(comment.added_at),
- "text" : comment.comment,
- "user_display_name" : comment_user.username,
- "user_url" : comment_user.get_profile_url(),
- "delete_url" : delete_url
- })
-
- data = simplejson.dumps(json_comments)
- return HttpResponse(data, mimetype="application/json")
-
-def delete_comment(request, object_id='', comment_id='', commented_object_type=None):
- response = None
- commented_object = None
- if commented_object_type == 'question':
- commented_object = Question
- elif commented_object_type == 'answer':
- commented_object = Answer
-
- if request.is_ajax():
- comment = get_object_or_404(Comment, id=comment_id)
- if auth.can_delete_comment(request.user, comment):
- obj = get_object_or_404(commented_object, id=object_id)
- obj.comments.remove(comment)
- obj.comment_count = obj.comment_count - 1
- obj.save()
- user = request.user
- return __generate_comments_json(obj, commented_object_type, user)
- raise PermissionDenied()
-
-def logout(request):
- return render_to_response('logout.html', {
- 'next' : get_next_url(request),
- }, context_instance=RequestContext(request))
-
-def badges(request):
- badges = Badge.objects.all().order_by('type')
- my_badges = []
- if request.user.is_authenticated():
- my_badges = Award.objects.filter(user=request.user)
- my_badges.query.group_by = ['badge_id']
-
- return render_to_response('badges.html', {
- 'badges' : badges,
- 'mybadges' : my_badges,
- 'feedback_faq_url' : reverse('feedback'),
- }, context_instance=RequestContext(request))
-
-def badge(request, id):
- badge = get_object_or_404(Badge, id=id)
- awards = Award.objects.extra(
- select={'id': 'auth_user.id',
- 'name': 'auth_user.username',
- 'rep':'auth_user.reputation',
- 'gold': 'auth_user.gold',
- 'silver': 'auth_user.silver',
- 'bronze': 'auth_user.bronze'},
- tables=['award', 'auth_user'],
- where=['badge_id=%s AND user_id=auth_user.id'],
- params=[id]
- ).distinct('id')
-
- return render_to_response('badge.html', {
- 'awards' : awards,
- 'badge' : badge,
- }, context_instance=RequestContext(request))
-
-def read_message(request):
- if request.method == "POST":
- if request.POST['formdata'] == 'required':
- request.session['message_silent'] = 1
- if request.user.is_authenticated():
- request.user.delete_messages()
- return HttpResponse('')
-
-def upload(request):
- class FileTypeNotAllow(Exception):
- pass
- class FileSizeNotAllow(Exception):
- pass
- class UploadPermissionNotAuthorized(Exception):
- pass
-
- #<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>
- xml_template = "<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>"
-
- try:
- f = request.FILES['file-upload']
- # check upload permission
- if not can_upload_files(request.user):
- raise UploadPermissionNotAuthorized
-
- # check file type
- file_name_suffix = os.path.splitext(f.name)[1].lower()
- if not file_name_suffix in settings.ALLOW_FILE_TYPES:
- raise FileTypeNotAllow
-
- # generate new file name
- new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix
- # use default storage to store file
- default_storage.save(new_file_name, f)
- # check file size
- # byte
- size = default_storage.size(new_file_name)
- if size > settings.ALLOW_MAX_FILE_SIZE:
- default_storage.delete(new_file_name)
- raise FileSizeNotAllow
-
- result = xml_template % ('Good', '', default_storage.url(new_file_name))
- except UploadPermissionNotAuthorized:
- result = xml_template % ('', _('uploading images is limited to users with >60 reputation points'), '')
- except FileTypeNotAllow:
- result = xml_template % ('', _("allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"), '')
- except FileSizeNotAllow:
- result = xml_template % ('', _("maximum upload file size is %sK") % settings.ALLOW_MAX_FILE_SIZE / 1024, '')
- except Exception:
- result = xml_template % ('', _('Error uploading file. Please contact the site administrator. Thank you. %s' % Exception), '')
-
- return HttpResponse(result, mimetype="application/xml")
-
-def books(request):
- return HttpResponseRedirect(reverse('books') + '/mysql-zhaoyang')
-
-def book(request, short_name, unanswered=False):
- """
- 1. questions list
- 2. book info
- 3. author info and blog rss items
- """
- """
- List of Questions, Tagged questions, and Unanswered questions.
- """
- books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
- match_count = len(books)
- if match_count == 0:
- raise Http404
- else:
- # the book info
- book = books[0]
- # get author info
- author_info = BookAuthorInfo.objects.get(book=book)
- # get author rss info
- author_rss = BookAuthorRss.objects.filter(book=book)
-
- # get pagesize from session, if failed then get default value
- user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE)
- # set pagesize equal to logon user specified value in database
- if request.user.is_authenticated() and request.user.questions_per_page > 0:
- user_page_size = request.user.questions_per_page
-
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
-
- view_id = request.GET.get('sort', None)
- view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
- try:
- orderby = view_dic[view_id]
- except KeyError:
- view_id = "latest"
- orderby = "-added_at"
-
- # check if request is from tagged questions
- if unanswered:
- # check if request is from unanswered questions
- # Article.objects.filter(publications__id__exact=1)
- objects = Question.objects.filter(book__id__exact=book.id, deleted=False, answer_count=0).order_by(orderby)
- else:
- objects = Question.objects.filter(book__id__exact=book.id, deleted=False).order_by(orderby)
-
- # RISK - inner join queries
- objects = objects.select_related();
- objects_list = Paginator(objects, user_page_size)
- questions = objects_list.page(page)
-
- return render_to_response('book.html', {
- "book" : book,
- "author_info" : author_info,
- "author_rss" : author_rss,
- "questions" : questions,
- "context" : {
- 'is_paginated' : True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': questions.has_previous(),
- 'has_next': questions.has_next(),
- 'previous': questions.previous_page_number(),
- 'next': questions.next_page_number(),
- 'base_url' : request.path + '?sort=%s&' % view_id,
- 'pagesize' : user_page_size
- }
- }, context_instance=RequestContext(request))
-
-@login_required
-def ask_book(request, short_name):
- if request.method == "POST":
- form = AskForm(request.POST)
- if form.is_valid():
- added_at = datetime.datetime.now()
- html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
- question = Question(
- title = strip_tags(form.cleaned_data['title']),
- author = request.user,
- added_at = added_at,
- last_activity_at = added_at,
- last_activity_by = request.user,
- wiki = form.cleaned_data['wiki'],
- tagnames = form.cleaned_data['tags'].strip(),
- html = html,
- summary = strip_tags(html)[:120]
- )
- if question.wiki:
- question.last_edited_by = question.author
- question.last_edited_at = added_at
- question.wikified_at = added_at
-
- question.save()
-
- # create the first revision
- QuestionRevision.objects.create(
- question = question,
- revision = 1,
- title = question.title,
- author = request.user,
- revised_at = added_at,
- tagnames = question.tagnames,
- summary = CONST['default_version'],
- text = form.cleaned_data['text']
- )
-
- books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
- match_count = len(books)
- if match_count == 1:
- # the book info
- book = books[0]
- book.questions.add(question)
-
- return HttpResponseRedirect(question.get_absolute_url())
- else:
- form = AskForm()
-
- tags = _get_tags_cache_json()
- return render_to_response('ask.html', {
- 'form' : form,
- 'tags' : tags,
- 'email_validation_faq_url': reverse('faq') + '#validate',
- }, context_instance=RequestContext(request))
-
-def search(request):
- """
- Search by question, user and tag keywords.
- For questions now we only search keywords in question title.
- """
- if request.method == "GET":
- keywords = request.GET.get("q")
- search_type = request.GET.get("t")
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
- if keywords is None:
- return HttpResponseRedirect(reverse(index))
- if search_type == 'tag':
- return HttpResponseRedirect(reverse('tags') + '?q=%s&page=%s' % (keywords.strip(), page))
- elif search_type == "user":
- return HttpResponseRedirect(reverse('users') + '?q=%s&page=%s' % (keywords.strip(), page))
- elif search_type == "question":
-
- template_file = "questions.html"
- # 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
- user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE)
- # set pagesize equal to logon user specified value in database
- if request.user.is_authenticated() and request.user.questions_per_page > 0:
- user_page_size = request.user.questions_per_page
-
- try:
- page = int(request.GET.get('page', '1'))
- # get new pagesize from UI selection
- pagesize = int(request.GET.get('pagesize', user_page_size))
- if pagesize <> user_page_size:
- pagesize_changed = True
-
- except ValueError:
- page = 1
- pagesize = user_page_size
-
- # save this pagesize to user database
- if pagesize_changed:
- request.session["pagesize"] = pagesize
- if request.user.is_authenticated():
- user = request.user
- user.questions_per_page = pagesize
- user.save()
-
- view_id = request.GET.get('sort', None)
- view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
- try:
- orderby = view_dic[view_id]
- except KeyError:
- view_id = "latest"
- orderby = "-added_at"
-
- if settings.USE_PG_FTS:
- objects = Question.objects.filter(deleted=False).extra(
- select={
- 'ranking': "ts_rank_cd(tsv, plainto_tsquery(%s), 32)",
- },
- where=["tsv @@ plainto_tsquery(%s)"],
- params=[keywords],
- select_params=[keywords]
- ).order_by('-ranking')
-
- elif settings.USE_SPHINX_SEARCH == True:
- #search index is now free of delete questions and answers
- #so there is not "antideleted" filtering here
- objects = Question.search.query(keywords)
- #no related selection either because we're relying on full text search here
- else:
- objects = Question.objects.filter(deleted=False).extra(where=['title like %s'], params=['%' + keywords + '%']).order_by(orderby)
- # RISK - inner join queries
- objects = objects.select_related();
-
- objects_list = Paginator(objects, pagesize)
- questions = objects_list.page(page)
-
- # Get related tags from this page objects
- related_tags = []
- for question in questions.object_list:
- tags = list(question.tags.all())
- for tag in tags:
- if tag not in related_tags:
- related_tags.append(tag)
-
- #if is_search is true in the context, prepend this string to soting tabs urls
- search_uri = "?q=%s&page=%d&t=question" % ("+".join(keywords.split()), page)
-
- return render_to_response(template_file, {
- "questions" : questions,
- "tab_id" : view_id,
- "questions_count" : objects_list.count,
- "tags" : related_tags,
- "searchtag" : None,
- "searchtitle" : keywords,
- "keywords" : keywords,
- "is_unanswered" : False,
- "is_search": True,
- "search_uri": search_uri,
- "context" : {
- 'is_paginated' : True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': questions.has_previous(),
- 'has_next': questions.has_next(),
- 'previous': questions.previous_page_number(),
- 'next': questions.next_page_number(),
- 'base_url' : request.path + '?t=question&q=%s&sort=%s&' % (keywords, view_id),
- 'pagesize' : pagesize
- }}, context_instance=RequestContext(request))
-
- else:
- raise Http404
diff --git a/forum/views/README b/forum/views/README
new file mode 100644
index 00000000..7b6201cd
--- /dev/null
+++ b/forum/views/README
@@ -0,0 +1,12 @@
+readers.py - views strictly reading main content: questions, answers, tags and comments
+
+writers.py - views that write main content, with possible reading
+ note: deletion counts as writing in this case
+
+commands.py - data status changing commands, votes, question close/reopen
+
+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
diff --git a/forum/views/__init__.py b/forum/views/__init__.py
new file mode 100644
index 00000000..291fee2a
--- /dev/null
+++ b/forum/views/__init__.py
@@ -0,0 +1,5 @@
+import readers
+import writers
+import commands
+import users
+import meta
diff --git a/forum/views/commands.py b/forum/views/commands.py
new file mode 100644
index 00000000..88c2c077
--- /dev/null
+++ b/forum/views/commands.py
@@ -0,0 +1,335 @@
+import datetime
+from django.conf import settings
+from django.utils import simplejson
+from django.http import HttpResponse, HttpResponseRedirect
+from django.shortcuts import get_object_or_404
+from django.utils.translation import ugettext as _
+from django.template import RequestContext
+from forum.models import *
+from forum.forms import CloseForm
+from forum import auth
+from django.core.urlresolvers import reverse
+from django.contrib.auth.decorators import login_required
+from forum.utils.decorators import ajax_method, ajax_login_required
+import logging
+
+def vote(request, id):#refactor - pretty incomprehensible view used by various ajax calls
+#issues: this subroutine is too long, contains many magic numbers and other issues
+#it's called "vote" but many actions processed here have nothing to do with voting
+ """
+ vote_type:
+ acceptAnswer : 0,
+ questionUpVote : 1,
+ questionDownVote : 2,
+ favorite : 4,
+ answerUpVote: 5,
+ answerDownVote:6,
+ offensiveQuestion : 7,
+ offensiveAnswer:8,
+ removeQuestion: 9,
+ removeAnswer:10
+ questionSubscribeUpdates:11
+ questionUnSubscribeUpdates:12
+
+ accept answer code:
+ response_data['allowed'] = -1, Accept his own answer 0, no allowed - Anonymous 1, Allowed - by default
+ response_data['success'] = 0, failed 1, Success - by default
+ response_data['status'] = 0, By default 1, Answer has been accepted already(Cancel)
+
+ vote code:
+ allowed = -3, Don't have enough votes left
+ -2, Don't have enough reputation score
+ -1, Vote his own post
+ 0, no allowed - Anonymous
+ 1, Allowed - by default
+ status = 0, By default
+ 1, Cancel
+ 2, Vote is too old to be canceled
+
+ offensive code:
+ allowed = -3, Don't have enough flags left
+ -2, Don't have enough reputation score to do this
+ 0, not allowed
+ 1, allowed
+ status = 0, by default
+ 1, can't do it again
+ """
+ response_data = {
+ "allowed": 1,
+ "success": 1,
+ "status" : 0,
+ "count" : 0,
+ "message" : ''
+ }
+
+ def __can_vote(vote_score, user):#refactor - belongs to auth.py
+ if vote_score == 1:#refactor magic number
+ return auth.can_vote_up(request.user)
+ else:
+ return auth.can_vote_down(request.user)
+
+ try:
+ if not request.user.is_authenticated():
+ response_data['allowed'] = 0
+ response_data['success'] = 0
+
+ elif request.is_ajax() and request.method == 'POST':
+ question = get_object_or_404(Question, id=id)
+ vote_type = request.POST.get('type')
+
+ #accept answer
+ if vote_type == '0':
+ answer_id = request.POST.get('postId')
+ answer = get_object_or_404(Answer, id=answer_id)
+ # make sure question author is current user
+ if question.author == request.user:
+ # answer user who is also question author is not allow to accept answer
+ if answer.author == question.author:
+ response_data['success'] = 0
+ response_data['allowed'] = -1
+ # check if answer has been accepted already
+ elif answer.accepted:
+ auth.onAnswerAcceptCanceled(answer, request.user)
+ response_data['status'] = 1
+ else:
+ # set other answers in this question not accepted first
+ for answer_of_question in Answer.objects.get_answers_from_question(question, request.user):
+ if answer_of_question != answer and answer_of_question.accepted:
+ auth.onAnswerAcceptCanceled(answer_of_question, request.user)
+
+ #make sure retrieve data again after above author changes, they may have related data
+ answer = get_object_or_404(Answer, id=answer_id)
+ auth.onAnswerAccept(answer, request.user)
+ else:
+ response_data['allowed'] = 0
+ response_data['success'] = 0
+ # favorite
+ elif vote_type == '4':
+ has_favorited = False
+ fav_questions = FavoriteQuestion.objects.filter(question=question)
+ # if the same question has been favorited before, then delete it
+ if fav_questions is not None:
+ for item in fav_questions:
+ if item.user == request.user:
+ item.delete()
+ response_data['status'] = 1
+ response_data['count'] = len(fav_questions) - 1
+ if response_data['count'] < 0:
+ response_data['count'] = 0
+ has_favorited = True
+ # if above deletion has not been executed, just insert a new favorite question
+ if not has_favorited:
+ new_item = FavoriteQuestion(question=question, user=request.user)
+ new_item.save()
+ response_data['count'] = FavoriteQuestion.objects.filter(question=question).count()
+ Question.objects.update_favorite_count(question)
+
+ elif vote_type in ['1', '2', '5', '6']:
+ post_id = id
+ post = question
+ vote_score = 1
+ if vote_type in ['5', '6']:
+ answer_id = request.POST.get('postId')
+ answer = get_object_or_404(Answer, id=answer_id)
+ post_id = answer_id
+ post = answer
+ if vote_type in ['2', '6']:
+ vote_score = -1
+
+ if post.author == request.user:
+ response_data['allowed'] = -1
+ elif not __can_vote(vote_score, request.user):
+ response_data['allowed'] = -2
+ elif post.votes.filter(user=request.user).count() > 0:
+ vote = post.votes.filter(user=request.user)[0]
+ # unvote should be less than certain time
+ if (datetime.datetime.now().day - vote.voted_at.day) >= auth.VOTE_RULES['scope_deny_unvote_days']:
+ response_data['status'] = 2
+ else:
+ voted = vote.vote
+ if voted > 0:
+ # cancel upvote
+ auth.onUpVotedCanceled(vote, post, request.user)
+
+ else:
+ # cancel downvote
+ auth.onDownVotedCanceled(vote, post, request.user)
+
+ response_data['status'] = 1
+ response_data['count'] = post.score
+ elif Vote.objects.get_votes_count_today_from_user(request.user) >= auth.VOTE_RULES['scope_votes_per_user_per_day']:
+ response_data['allowed'] = -3
+ else:
+ vote = Vote(user=request.user, content_object=post, vote=vote_score, voted_at=datetime.datetime.now())
+ if vote_score > 0:
+ # upvote
+ auth.onUpVoted(vote, post, request.user)
+ else:
+ # downvote
+ auth.onDownVoted(vote, post, request.user)
+
+ votes_left = auth.VOTE_RULES['scope_votes_per_user_per_day'] - Vote.objects.get_votes_count_today_from_user(request.user)
+ if votes_left <= auth.VOTE_RULES['scope_warn_votes_left']:
+ response_data['message'] = u'%s votes left' % votes_left
+ response_data['count'] = post.score
+ elif vote_type in ['7', '8']:
+ post = question
+ post_id = id
+ if vote_type == '8':
+ post_id = request.POST.get('postId')
+ post = get_object_or_404(Answer, id=post_id)
+
+ if FlaggedItem.objects.get_flagged_items_count_today(request.user) >= auth.VOTE_RULES['scope_flags_per_user_per_day']:
+ response_data['allowed'] = -3
+ elif not auth.can_flag_offensive(request.user):
+ response_data['allowed'] = -2
+ elif post.flagged_items.filter(user=request.user).count() > 0:
+ response_data['status'] = 1
+ else:
+ item = FlaggedItem(user=request.user, content_object=post, flagged_at=datetime.datetime.now())
+ auth.onFlaggedItem(item, post, request.user)
+ response_data['count'] = post.offensive_flag_count
+ # send signal when question or answer be marked offensive
+ mark_offensive.send(sender=post.__class__, instance=post, mark_by=request.user)
+ elif vote_type in ['9', '10']:
+ post = question
+ post_id = id
+ if vote_type == '10':
+ post_id = request.POST.get('postId')
+ post = get_object_or_404(Answer, id=post_id)
+
+ if not auth.can_delete_post(request.user, post):
+ response_data['allowed'] = -2
+ elif post.deleted == True:
+ logging.debug('debug restoring post in view')
+ auth.onDeleteCanceled(post, request.user)
+ response_data['status'] = 1
+ else:
+ auth.onDeleted(post, request.user)
+ delete_post_or_answer.send(sender=post.__class__, instance=post, delete_by=request.user)
+ elif vote_type == '11':#subscribe q updates
+ user = request.user
+ if user.is_authenticated():
+ if user not in question.followed_by.all():
+ question.followed_by.add(user)
+ if settings.EMAIL_VALIDATION == 'on' and user.email_isvalid == False:
+ response_data['message'] = \
+ _('subscription saved, %(email)s needs validation, see %(details_url)s') \
+ % {'email':user.email,'details_url':reverse('faq') + '#validate'}
+ feed_setting = EmailFeedSetting.objects.get(subscriber=user,feed_type='q_sel')
+ if feed_setting.frequency == 'n':
+ feed_setting.frequency = 'd'
+ feed_setting.save()
+ if 'message' in response_data:
+ response_data['message'] += '<br/>'
+ response_data['message'] = _('email update frequency has been set to daily')
+ #response_data['status'] = 1
+ #responst_data['allowed'] = 1
+ else:
+ pass
+ #response_data['status'] = 0
+ #response_data['allowed'] = 0
+ elif vote_type == '12':#unsubscribe q updates
+ user = request.user
+ if user.is_authenticated():
+ if user in question.followed_by.all():
+ question.followed_by.remove(user)
+ else:
+ response_data['success'] = 0
+ response_data['message'] = u'Request mode is not supported. Please try again.'
+
+ data = simplejson.dumps(response_data)
+
+ except Exception, e:
+ response_data['message'] = str(e)
+ data = simplejson.dumps(response_data)
+ return HttpResponse(data, mimetype="application/json")
+
+#internally grouped views - used by the tagging system
+@ajax_login_required
+def mark_tag(request, tag=None, **kwargs):#tagging system
+ action = kwargs['action']
+ ts = MarkedTag.objects.filter(user=request.user, tag__name=tag)
+ if action == 'remove':
+ logging.debug('deleting tag %s' % tag)
+ ts.delete()
+ else:
+ reason = kwargs['reason']
+ if len(ts) == 0:
+ try:
+ t = Tag.objects.get(name=tag)
+ mt = MarkedTag(user=request.user, reason=reason, tag=t)
+ mt.save()
+ except:
+ pass
+ else:
+ ts.update(reason=reason)
+ return HttpResponse(simplejson.dumps(''), mimetype="application/json")
+
+@ajax_login_required
+def ajax_toggle_ignored_questions(request):#ajax tagging and tag-filtering system
+ if request.user.hide_ignored_questions:
+ new_hide_setting = False
+ else:
+ new_hide_setting = True
+ request.user.hide_ignored_questions = new_hide_setting
+ request.user.save()
+
+@ajax_method
+def ajax_command(request):#refactor? view processing ajax commands - note "vote" and view others do it too
+ if 'command' not in request.POST:
+ return HttpResponseForbidden(mimetype="application/json")
+ if request.POST['command'] == 'toggle-ignored-questions':
+ return ajax_toggle_ignored_questions(request)
+
+@login_required
+def close(request, id):#close question
+ """view to initiate and process
+ question close
+ """
+ question = get_object_or_404(Question, id=id)
+ if not auth.can_close_question(request.user, question):
+ return HttpResponse('Permission denied.')
+ if request.method == 'POST':
+ form = CloseForm(request.POST)
+ if form.is_valid():
+ reason = form.cleaned_data['reason']
+ question.closed = True
+ question.closed_by = request.user
+ question.closed_at = datetime.datetime.now()
+ question.close_reason = reason
+ question.save()
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ form = CloseForm()
+ return render_to_response('close.html', {
+ 'form' : form,
+ 'question' : question,
+ }, context_instance=RequestContext(request))
+
+@login_required
+def reopen(request, id):#re-open question
+ """view to initiate and process
+ question close
+ """
+ question = get_object_or_404(Question, id=id)
+ # open question
+ if not auth.can_reopen_question(request.user, question):
+ return HttpResponse('Permission denied.')
+ if request.method == 'POST' :
+ Question.objects.filter(id=question.id).update(closed=False,
+ closed_by=None, closed_at=None, close_reason=None)
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ return render_to_response('reopen.html', {
+ 'question' : question,
+ }, context_instance=RequestContext(request))
+
+#osqa-user communication system
+def read_message(request):#marks message a read
+ if request.method == "POST":
+ if request.POST['formdata'] == 'required':
+ request.session['message_silent'] = 1
+ if request.user.is_authenticated():
+ request.user.delete_messages()
+ return HttpResponse('')
diff --git a/forum/views/meta.py b/forum/views/meta.py
new file mode 100644
index 00000000..b4c7a37f
--- /dev/null
+++ b/forum/views/meta.py
@@ -0,0 +1,91 @@
+from django.shortcuts import render_to_response, get_object_or_404
+from django.core.urlresolvers import reverse
+from django.template import RequestContext
+from django.http import HttpResponseRedirect, HttpResponse
+from forum.forms import FeedbackForm
+from django.core.urlresolvers import reverse
+from django.core.mail import mail_admins
+from django.utils.translation import ugettext as _
+from forum.utils.forms import get_next_url
+from forum.models import Badge, Award
+
+def about(request):
+ return render_to_response('about.html', context_instance=RequestContext(request))
+
+def faq(request):
+ data = {
+ 'gravatar_faq_url': reverse('faq') + '#gravatar',
+ 'send_email_key_url': reverse('send_email_key'),
+ 'ask_question_url': reverse('ask'),
+ }
+ return render_to_response('faq.html', data, context_instance=RequestContext(request))
+
+def feedback(request):
+ data = {}
+ form = None
+ if request.method == "POST":
+ form = FeedbackForm(request.POST)
+ if form.is_valid():
+ if not request.user.is_authenticated:
+ data['email'] = form.cleaned_data.get('email',None)
+ data['message'] = form.cleaned_data['message']
+ data['name'] = form.cleaned_data.get('name',None)
+ message = render_to_response('feedback_email.txt',data,context_instance=RequestContext(request))
+ mail_admins(_('Q&A forum feedback'), message)
+ msg = _('Thanks for the feedback!')
+ request.user.message_set.create(message=msg)
+ return HttpResponseRedirect(get_next_url(request))
+ else:
+ form = FeedbackForm(initial={'next':get_next_url(request)})
+
+ data['form'] = form
+ return render_to_response('feedback.html', data, context_instance=RequestContext(request))
+feedback.CANCEL_MESSAGE=_('We look forward to hearing your feedback! Please, give it next time :)')
+
+def privacy(request):
+ return render_to_response('privacy.html', context_instance=RequestContext(request))
+
+def logout(request):#refactor/change behavior?
+#currently you click logout and you get
+#to this view which actually asks you again - do you really want to log out?
+#I guess rationale was to tell the user that s/he may be still logged in
+#through their external login sytem and we'd want to remind them about it
+#however it might be a little annoying
+#why not just show a message: you are logged out of osqa, but
+#if you really want to log out -> go to your openid provider
+ return render_to_response('logout.html', {
+ 'next' : get_next_url(request),
+ }, context_instance=RequestContext(request))
+
+def badges(request):#user status/reputation system
+ badges = Badge.objects.all().order_by('type')
+ my_badges = []
+ if request.user.is_authenticated():
+ my_badges = Award.objects.filter(user=request.user).values('badge_id')
+ #my_badges.query.group_by = ['badge_id']
+
+ return render_to_response('badges.html', {
+ 'badges' : badges,
+ 'mybadges' : my_badges,
+ 'feedback_faq_url' : reverse('feedback'),
+ }, context_instance=RequestContext(request))
+
+def badge(request, id):
+ badge = get_object_or_404(Badge, id=id)
+ awards = Award.objects.extra(
+ select={'id': 'auth_user.id',
+ 'name': 'auth_user.username',
+ 'rep':'auth_user.reputation',
+ 'gold': 'auth_user.gold',
+ 'silver': 'auth_user.silver',
+ 'bronze': 'auth_user.bronze'},
+ tables=['award', 'auth_user'],
+ where=['badge_id=%s AND user_id=auth_user.id'],
+ params=[id]
+ ).distinct('id')
+
+ return render_to_response('badge.html', {
+ 'awards' : awards,
+ 'badge' : badge,
+ }, context_instance=RequestContext(request))
+
diff --git a/forum/views/readers.py b/forum/views/readers.py
new file mode 100644
index 00000000..6b0da476
--- /dev/null
+++ b/forum/views/readers.py
@@ -0,0 +1,588 @@
+# encoding:utf-8
+import datetime
+import logging
+from urllib import unquote
+from django.conf import settings
+from django.shortcuts import render_to_response, get_object_or_404
+from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
+from django.core.paginator import Paginator, EmptyPage, InvalidPage
+from django.template import RequestContext
+from django.utils.html import *
+from django.utils import simplejson
+from django.db.models import Q
+from django.utils.translation import ugettext as _
+from django.template.defaultfilters import slugify
+from django.core.urlresolvers import reverse
+from django.utils.datastructures import SortedDict
+
+from forum.utils.html import sanitize_html
+from markdown2 import Markdown
+#from lxml.html.diff import htmldiff
+from forum.utils.diff import textDiff as htmldiff
+from forum.forms import *
+from forum.models import *
+from forum.auth import *
+from forum.const import *
+from forum import auth
+from forum.utils.forms import get_next_url
+
+# used in index page
+#refactor - move these numbers somewhere?
+INDEX_PAGE_SIZE = 30
+INDEX_AWARD_SIZE = 15
+INDEX_TAGS_SIZE = 25
+# used in tags list
+DEFAULT_PAGE_SIZE = 60
+# used in questions
+QUESTIONS_PAGE_SIZE = 30
+# used in answers
+ANSWERS_PAGE_SIZE = 10
+
+markdowner = Markdown(html4tags=True)
+
+#system to display main content
+def _get_tags_cache_json():#service routine used by views requiring tag list in the javascript space
+ """returns list of all tags in json format
+ no caching yet, actually
+ """
+ tags = Tag.objects.filter(deleted=False).all()
+ tags_list = []
+ for tag in tags:
+ dic = {'n': tag.name, 'c': tag.used_count}
+ tags_list.append(dic)
+ tags = simplejson.dumps(tags_list)
+ return tags
+
+def _get_and_remember_questions_sort_method(request, view_dic, default):#service routine used by q listing views and question view
+ """manages persistence of post sort order
+ it is assumed that when user wants newest question -
+ then he/she wants newest answers as well, etc.
+ how far should this assumption actually go - may be a good question
+ """
+ if default not in view_dic:
+ raise Exception('default value must be in view_dic')
+
+ q_sort_method = request.REQUEST.get('sort', None)
+ if q_sort_method == None:
+ q_sort_method = request.session.get('questions_sort_method', default)
+
+ if q_sort_method not in view_dic:
+ q_sort_method = default
+ request.session['questions_sort_method'] = q_sort_method
+ return q_sort_method, view_dic[q_sort_method]
+
+#refactor? - we have these
+#views that generate a listing of questions in one way or another:
+#index, unanswered, questions, search, tag
+#should we dry them up?
+#related topics - information drill-down, search refinement
+
+def index(request):#generates front page - shows listing of questions sorted in various ways
+ """index view mapped to the root url of the Q&A site
+ """
+ view_dic = {
+ "latest":"-last_activity_at",
+ "hottest":"-answer_count",
+ "mostvoted":"-score",
+ }
+ view_id, orderby = _get_and_remember_questions_sort_method(request, view_dic, 'latest')
+
+ pagesize = request.session.get("pagesize",QUESTIONS_PAGE_SIZE)
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+
+ qs = Question.objects.exclude(deleted=True).order_by(orderby)
+
+ objects_list = Paginator(qs, pagesize)
+ questions = objects_list.page(page)
+
+ # RISK - inner join queries
+ #questions = questions.select_related()
+ tags = Tag.objects.get_valid_tags(INDEX_TAGS_SIZE)
+
+ awards = Award.objects.get_recent_awards()
+
+ (interesting_tag_names, ignored_tag_names) = (None, None)
+ if request.user.is_authenticated():
+ pt = MarkedTag.objects.filter(user=request.user)
+ interesting_tag_names = pt.filter(reason='good').values_list('tag__name', flat=True)
+ ignored_tag_names = pt.filter(reason='bad').values_list('tag__name', flat=True)
+
+ tags_autocomplete = _get_tags_cache_json()
+
+ return render_to_response('index.html', {
+ 'interesting_tag_names': interesting_tag_names,
+ 'tags_autocomplete': tags_autocomplete,
+ 'ignored_tag_names': ignored_tag_names,
+ "questions" : questions,
+ "tab_id" : view_id,
+ "tags" : tags,
+ "awards" : awards[:INDEX_AWARD_SIZE],
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': questions.has_previous(),
+ 'has_next': questions.has_next(),
+ 'previous': questions.previous_page_number(),
+ 'next': questions.next_page_number(),
+ 'base_url' : request.path + '?sort=%s&' % view_id,
+ 'pagesize' : pagesize
+ }}, context_instance=RequestContext(request))
+
+def unanswered(request):#generates listing of unanswered questions
+ return questions(request, unanswered=True)
+
+def questions(request, tagname=None, unanswered=False):#a view generating listing of questions, used by 'unanswered' too
+ """
+ List of Questions, Tagged questions, and Unanswered questions.
+ """
+ # template file
+ # "questions.html" or maybe index.html in the future
+ template_file = "questions.html"
+ # 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)
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+
+ view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
+ view_id, orderby = _get_and_remember_questions_sort_method(request,view_dic,'latest')
+
+ # check if request is from tagged questions
+ qs = Question.objects.exclude(deleted=True)
+
+ if tagname is not None:
+ qs = qs.filter(tags__name = unquote(tagname))
+
+ if unanswered:
+ qs = qs.exclude(answer_accepted=True)
+
+ author_name = None
+ #user contributed questions & answers
+ if 'user' in request.GET:
+ try:
+ author_name = request.GET['user']
+ u = User.objects.get(username=author_name)
+ qs = qs.filter(Q(author=u) | Q(answers__author=u))
+ except User.DoesNotExist:
+ author_name = None
+
+ if request.user.is_authenticated():
+ uid_str = str(request.user.id)
+ qs = qs.extra(
+ select = SortedDict([
+ (
+ 'interesting_score',
+ 'SELECT COUNT(1) FROM forum_markedtag, question_tags '
+ + 'WHERE forum_markedtag.user_id = %s '
+ + 'AND forum_markedtag.tag_id = question_tags.tag_id '
+ + 'AND forum_markedtag.reason = \'good\' '
+ + 'AND question_tags.question_id = question.id'
+ ),
+ ]),
+ select_params = (uid_str,),
+ )
+ if request.user.hide_ignored_questions:
+ ignored_tags = Tag.objects.filter(user_selections__reason='bad',
+ user_selections__user = request.user)
+ qs = qs.exclude(tags__in=ignored_tags)
+ else:
+ qs = qs.extra(
+ select = SortedDict([
+ (
+ 'ignored_score',
+ 'SELECT COUNT(1) FROM forum_markedtag, question_tags '
+ + 'WHERE forum_markedtag.user_id = %s '
+ + 'AND forum_markedtag.tag_id = question_tags.tag_id '
+ + 'AND forum_markedtag.reason = \'bad\' '
+ + 'AND question_tags.question_id = question.id'
+ )
+ ]),
+ select_params = (uid_str, )
+ )
+
+ qs = qs.select_related(depth=1).order_by(orderby)
+
+ objects_list = Paginator(qs, pagesize)
+ questions = objects_list.page(page)
+
+ # Get related tags from this page objects
+ if questions.object_list.count() > 0:
+ related_tags = Tag.objects.get_tags_by_questions(questions.object_list)
+ else:
+ related_tags = None
+ tags_autocomplete = _get_tags_cache_json()
+
+ # get the list of interesting and ignored tags
+ (interesting_tag_names, ignored_tag_names) = (None, None)
+ if request.user.is_authenticated():
+ pt = MarkedTag.objects.filter(user=request.user)
+ interesting_tag_names = pt.filter(reason='good').values_list('tag__name', flat=True)
+ ignored_tag_names = pt.filter(reason='bad').values_list('tag__name', flat=True)
+
+ return render_to_response(template_file, {
+ "questions" : questions,
+ "author_name" : author_name,
+ "tab_id" : view_id,
+ "questions_count" : objects_list.count,
+ "tags" : related_tags,
+ "tags_autocomplete" : tags_autocomplete,
+ "searchtag" : tagname,
+ "is_unanswered" : unanswered,
+ "interesting_tag_names": interesting_tag_names,
+ 'ignored_tag_names': ignored_tag_names,
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': questions.has_previous(),
+ 'has_next': questions.has_next(),
+ 'previous': questions.previous_page_number(),
+ 'next': questions.next_page_number(),
+ 'base_url' : request.path + '?sort=%s&' % view_id,
+ 'pagesize' : pagesize
+ }}, context_instance=RequestContext(request))
+
+def search(request): #generates listing of questions matching a search query - including tags and just words
+ """generates listing of questions matching a search query
+ supports full text search in mysql db using sphinx and internally in postgresql
+ falls back on simple partial string matching approach if
+ full text search function is not available
+ """
+ if request.method == "GET":
+ keywords = request.GET.get("q")
+ search_type = request.GET.get("t")
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+ if keywords is None:
+ return HttpResponseRedirect(reverse(index))
+ if search_type == 'tag':
+ return HttpResponseRedirect(reverse('tags') + '?q=%s&page=%s' % (keywords.strip(), page))
+ elif search_type == "user":
+ return HttpResponseRedirect(reverse('users') + '?q=%s&page=%s' % (keywords.strip(), page))
+ elif search_type == "question":
+
+ template_file = "questions.html"
+ # 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
+ user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE)
+ # set pagesize equal to logon user specified value in database
+ if request.user.is_authenticated() and request.user.questions_per_page > 0:
+ user_page_size = request.user.questions_per_page
+
+ try:
+ page = int(request.GET.get('page', '1'))
+ # get new pagesize from UI selection
+ pagesize = int(request.GET.get('pagesize', user_page_size))
+ if pagesize <> user_page_size:
+ pagesize_changed = True
+
+ except ValueError:
+ page = 1
+ pagesize = user_page_size
+
+ # save this pagesize to user database
+ if pagesize_changed:
+ request.session["pagesize"] = pagesize
+ if request.user.is_authenticated():
+ user = request.user
+ user.questions_per_page = pagesize
+ user.save()
+
+ view_id = request.GET.get('sort', None)
+ view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
+ try:
+ orderby = view_dic[view_id]
+ except KeyError:
+ view_id = "latest"
+ orderby = "-added_at"
+
+ def question_search(keywords, orderby):
+ objects = Question.objects.filter(deleted=False).extra(where=['title like %s'], params=['%' + keywords + '%']).order_by(orderby)
+ # RISK - inner join queries
+ return objects.select_related();
+
+ from forum.modules import get_handler
+
+ question_search = get_handler('question_search', question_search)
+
+ objects = question_search(keywords, orderby)
+
+ objects_list = Paginator(objects, pagesize)
+ questions = objects_list.page(page)
+
+ # Get related tags from this page objects
+ related_tags = []
+ for question in questions.object_list:
+ tags = list(question.tags.all())
+ for tag in tags:
+ if tag not in related_tags:
+ related_tags.append(tag)
+
+ #if is_search is true in the context, prepend this string to soting tabs urls
+ search_uri = "?q=%s&page=%d&t=question" % ("+".join(keywords.split()), page)
+
+ return render_to_response(template_file, {
+ "questions" : questions,
+ "tab_id" : view_id,
+ "questions_count" : objects_list.count,
+ "tags" : related_tags,
+ "searchtag" : None,
+ "searchtitle" : keywords,
+ "keywords" : keywords,
+ "is_unanswered" : False,
+ "is_search": True,
+ "search_uri": search_uri,
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': questions.has_previous(),
+ 'has_next': questions.has_next(),
+ 'previous': questions.previous_page_number(),
+ 'next': questions.next_page_number(),
+ 'base_url' : request.path + '?t=question&q=%s&sort=%s&' % (keywords, view_id),
+ 'pagesize' : pagesize
+ }}, context_instance=RequestContext(request))
+
+ else:
+ raise Http404
+
+def tag(request, tag):#stub generates listing of questions tagged with a single tag
+ return questions(request, tagname=tag)
+
+def tags(request):#view showing a listing of available tags - plain list
+ stag = ""
+ is_paginated = True
+ sortby = request.GET.get('sort', 'used')
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+
+ if request.method == "GET":
+ stag = request.GET.get("q", "").strip()
+ if stag != '':
+ objects_list = Paginator(Tag.objects.filter(deleted=False).exclude(used_count=0).extra(where=['name like %s'], params=['%' + stag + '%']), DEFAULT_PAGE_SIZE)
+ else:
+ if sortby == "name":
+ objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE)
+ else:
+ objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE)
+
+ try:
+ tags = objects_list.page(page)
+ except (EmptyPage, InvalidPage):
+ tags = objects_list.page(objects_list.num_pages)
+
+ return render_to_response('tags.html', {
+ "tags" : tags,
+ "stag" : stag,
+ "tab_id" : sortby,
+ "keywords" : stag,
+ "context" : {
+ 'is_paginated' : is_paginated,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': tags.has_previous(),
+ 'has_next': tags.has_next(),
+ 'previous': tags.previous_page_number(),
+ 'next': tags.next_page_number(),
+ 'base_url' : reverse('tags') + '?sort=%s&' % sortby
+ }
+ }, context_instance=RequestContext(request))
+
+def question(request, id):#refactor - long subroutine. display question body, answers and comments
+ """view that displays body of the question and
+ all answers to it
+ """
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+
+ view_id = request.GET.get('sort', None)
+ view_dic = {"latest":"-added_at", "oldest":"added_at", "votes":"-score" }
+ try:
+ orderby = view_dic[view_id]
+ except KeyError:
+ qsm = request.session.get('questions_sort_method',None)
+ if qsm in ('mostvoted','latest'):
+ logging.debug('loaded from session ' + qsm)
+ if qsm == 'mostvoted':
+ view_id = 'votes'
+ orderby = '-score'
+ else:
+ view_id = 'latest'
+ orderby = '-added_at'
+ else:
+ view_id = "votes"
+ orderby = "-score"
+
+ logging.debug('view_id=' + str(view_id))
+
+ question = get_object_or_404(Question, id=id)
+ try:
+ pattern = r'/%s%s%d/([\w-]+)' % (settings.FORUM_SCRIPT_ALIAS,_('question/'), question.id)
+ path_re = re.compile(pattern)
+ logging.debug(pattern)
+ logging.debug(request.path)
+ m = path_re.match(request.path)
+ if m:
+ slug = m.group(1)
+ logging.debug('have slug %s' % slug)
+ assert(slug == slugify(question.title))
+ else:
+ logging.debug('no match!')
+ except:
+ return HttpResponseRedirect(question.get_absolute_url())
+
+ if question.deleted and not auth.can_view_deleted_post(request.user, question):
+ raise Http404
+ answer_form = AnswerForm(question,request.user)
+ answers = Answer.objects.get_answers_from_question(question, request.user)
+ answers = answers.select_related(depth=1)
+
+ favorited = question.has_favorite_by_user(request.user)
+ if request.user.is_authenticated():
+ question_vote = question.votes.select_related().filter(user=request.user)
+ else:
+ question_vote = None #is this correct?
+ if question_vote is not None and question_vote.count() > 0:
+ question_vote = question_vote[0]
+
+ user_answer_votes = {}
+ for answer in answers:
+ vote = answer.get_user_vote(request.user)
+ if vote is not None and not user_answer_votes.has_key(answer.id):
+ vote_value = -1
+ if vote.is_upvote():
+ vote_value = 1
+ user_answer_votes[answer.id] = vote_value
+
+ if answers is not None:
+ answers = answers.order_by("-accepted", orderby)
+
+ filtered_answers = []
+ for answer in answers:
+ if answer.deleted == True:
+ if answer.author_id == request.user.id:
+ filtered_answers.append(answer)
+ else:
+ filtered_answers.append(answer)
+
+ objects_list = Paginator(filtered_answers, ANSWERS_PAGE_SIZE)
+ page_objects = objects_list.page(page)
+
+ #todo: merge view counts per user and per session
+ #1) view count per session
+ update_view_count = False
+ if 'question_view_times' not in request.session:
+ request.session['question_view_times'] = {}
+
+ last_seen = request.session['question_view_times'].get(question.id,None)
+ updated_when, updated_who = question.get_last_update_info()
+
+ if updated_who != request.user:
+ if last_seen:
+ if last_seen < updated_when:
+ update_view_count = True
+ else:
+ update_view_count = True
+
+ request.session['question_view_times'][question.id] = datetime.datetime.now()
+
+ if update_view_count:
+ question.view_count += 1
+ question.save()
+
+ #2) question view count per user
+ if request.user.is_authenticated():
+ try:
+ question_view = QuestionView.objects.get(who=request.user, question=question)
+ except QuestionView.DoesNotExist:
+ question_view = QuestionView(who=request.user, question=question)
+ question_view.when = datetime.datetime.now()
+ question_view.save()
+
+ return render_to_response('question.html', {
+ "question" : question,
+ "question_vote" : question_vote,
+ "question_comment_count":question.comments.count(),
+ "answer" : answer_form,
+ "answers" : page_objects.object_list,
+ "user_answer_votes": user_answer_votes,
+ "tags" : question.tags.all(),
+ "tab_id" : view_id,
+ "favorited" : favorited,
+ "similar_questions" : Question.objects.get_similar_questions(question),
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': page_objects.has_previous(),
+ 'has_next': page_objects.has_next(),
+ 'previous': page_objects.previous_page_number(),
+ 'next': page_objects.next_page_number(),
+ 'base_url' : request.path + '?sort=%s&' % view_id,
+ 'extend_url' : "#sort-top"
+ }
+ }, context_instance=RequestContext(request))
+
+QUESTION_REVISION_TEMPLATE = ('<h1>%(title)s</h1>\n'
+ '<div class="text">%(html)s</div>\n'
+ '<div class="tags">%(tags)s</div>')
+def question_revisions(request, id):
+ post = get_object_or_404(Question, id=id)
+ revisions = list(post.revisions.all())
+ revisions.reverse()
+ for i, revision in enumerate(revisions):
+ revision.html = QUESTION_REVISION_TEMPLATE % {
+ 'title': revision.title,
+ 'html': sanitize_html(markdowner.convert(revision.text)),
+ 'tags': ' '.join(['<a class="post-tag">%s</a>' % tag
+ for tag in revision.tagnames.split(' ')]),
+ }
+ if i > 0:
+ revisions[i].diff = htmldiff(revisions[i-1].html, revision.html)
+ else:
+ revisions[i].diff = QUESTION_REVISION_TEMPLATE % {
+ 'title': revisions[0].title,
+ 'html': sanitize_html(markdowner.convert(revisions[0].text)),
+ 'tags': ' '.join(['<a class="post-tag">%s</a>' % tag
+ for tag in revisions[0].tagnames.split(' ')]),
+ }
+ revisions[i].summary = _('initial version')
+ return render_to_response('revisions_question.html', {
+ 'post': post,
+ 'revisions': revisions,
+ }, context_instance=RequestContext(request))
+
+ANSWER_REVISION_TEMPLATE = ('<div class="text">%(html)s</div>')
+def answer_revisions(request, id):
+ post = get_object_or_404(Answer, id=id)
+ revisions = list(post.revisions.all())
+ revisions.reverse()
+ for i, revision in enumerate(revisions):
+ revision.html = ANSWER_REVISION_TEMPLATE % {
+ 'html': sanitize_html(markdowner.convert(revision.text))
+ }
+ if i > 0:
+ revisions[i].diff = htmldiff(revisions[i-1].html, revision.html)
+ else:
+ revisions[i].diff = revisions[i].text
+ revisions[i].summary = _('initial version')
+ return render_to_response('revisions_answer.html', {
+ 'post': post,
+ 'revisions': revisions,
+ }, context_instance=RequestContext(request))
+
diff --git a/forum/views/users.py b/forum/views/users.py
new file mode 100644
index 00000000..cc05c19e
--- /dev/null
+++ b/forum/views/users.py
@@ -0,0 +1,947 @@
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import User
+from django.core.paginator import Paginator, EmptyPage, InvalidPage
+from django.template.defaultfilters import slugify
+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.utils.translation import ugettext as _
+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 import auth
+import calendar
+from django.contrib.contenttypes.models import ContentType
+
+question_type = ContentType.objects.get_for_model(Question)
+answer_type = ContentType.objects.get_for_model(Answer)
+comment_type = ContentType.objects.get_for_model(Comment)
+question_revision_type = ContentType.objects.get_for_model(QuestionRevision)
+answer_revision_type = ContentType.objects.get_for_model(AnswerRevision)
+repute_type = ContentType.objects.get_for_model(Repute)
+question_type_id = question_type.id
+answer_type_id = answer_type.id
+comment_type_id = comment_type.id
+question_revision_type_id = question_revision_type.id
+answer_revision_type_id = answer_revision_type.id
+repute_type_id = repute_type.id
+
+USERS_PAGE_SIZE = 35# refactor - move to some constants file
+
+def users(request):
+ is_paginated = True
+ sortby = request.GET.get('sort', 'reputation')
+ suser = request.REQUEST.get('q', "")
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+
+ if suser == "":
+ if sortby == "newest":
+ objects_list = Paginator(User.objects.all().order_by('-date_joined'), USERS_PAGE_SIZE)
+ elif sortby == "last":
+ objects_list = Paginator(User.objects.all().order_by('date_joined'), USERS_PAGE_SIZE)
+ elif sortby == "user":
+ objects_list = Paginator(User.objects.all().order_by('username'), USERS_PAGE_SIZE)
+ # default
+ else:
+ objects_list = Paginator(User.objects.all().order_by('-reputation'), USERS_PAGE_SIZE)
+ base_url = reverse('users') + '?sort=%s&' % sortby
+ else:
+ sortby = "reputation"
+ objects_list = Paginator(User.objects.extra(where=['username like %s'], params=['%' + suser + '%']).order_by('-reputation'), USERS_PAGE_SIZE)
+ base_url = reverse('users') + '?name=%s&sort=%s&' % (suser, sortby)
+
+ try:
+ users = objects_list.page(page)
+ except (EmptyPage, InvalidPage):
+ users = objects_list.page(objects_list.num_pages)
+
+ return render_to_response('users.html', {
+ "users" : users,
+ "suser" : suser,
+ "keywords" : suser,
+ "tab_id" : sortby,
+ "context" : {
+ 'is_paginated' : is_paginated,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': users.has_previous(),
+ 'has_next': users.has_next(),
+ 'previous': users.previous_page_number(),
+ 'next': users.next_page_number(),
+ 'base_url' : base_url
+ }
+
+ }, context_instance=RequestContext(request))
+
+@login_required
+def moderate_user(request, id):
+ """ajax handler of user moderation
+ """
+ if not auth.can_moderate_users(request.user) or request.method != 'POST':
+ raise Http404
+ if not request.is_ajax():
+ return HttpResponseForbidden(mimetype="application/json")
+
+ user = get_object_or_404(User, id=id)
+ form = ModerateUserForm(request.POST, instance=user)
+
+ if form.is_valid():
+ form.save()
+ logging.debug('data saved')
+ response = HttpResponse(simplejson.dumps(''), mimetype="application/json")
+ else:
+ response = HttpResponseForbidden(mimetype="application/json")
+ return response
+
+@login_required
+def edit_user(request, id):
+ user = get_object_or_404(User, id=id)
+ if request.user != user:
+ raise Http404
+ if request.method == "POST":
+ form = EditUserForm(user, request.POST)
+ 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'])
+ user.real_name = sanitize_html(form.cleaned_data['realname'])
+ user.website = sanitize_html(form.cleaned_data['website'])
+ user.location = sanitize_html(form.cleaned_data['city'])
+ user.date_of_birth = sanitize_html(form.cleaned_data['birthday'])
+ if len(user.date_of_birth) == 0:
+ user.date_of_birth = '1900-01-01'
+ user.about = sanitize_html(form.cleaned_data['about'])
+
+ user.save()
+ # send user updated singal if full fields have been updated
+ if user.email and user.real_name and user.website and user.location and \
+ user.date_of_birth and user.about:
+ user_updated.send(sender=user.__class__, instance=user, updated_by=user)
+ return HttpResponseRedirect(user.get_profile_url())
+ else:
+ form = EditUserForm(user)
+ return render_to_response('user_edit.html', {
+ 'form' : form,
+ 'gravatar_faq_url' : reverse('faq') + '#gravatar',
+ }, context_instance=RequestContext(request))
+
+def user_stats(request, user_id, user_view):
+ user = get_object_or_404(User, id=user_id)
+ questions = Question.objects.extra(
+ select={
+ 'vote_count' : 'question.score',
+ 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id',
+ 'la_user_id' : 'auth_user.id',
+ 'la_username' : 'auth_user.username',
+ 'la_user_gold' : 'auth_user.gold',
+ 'la_user_silver' : 'auth_user.silver',
+ 'la_user_bronze' : 'auth_user.bronze',
+ 'la_user_reputation' : 'auth_user.reputation'
+ },
+ select_params=[user_id],
+ tables=['question', 'auth_user'],
+ where=['question.deleted=False AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'],
+ params=[user_id],
+ order_by=['-vote_count', '-last_activity_at']
+ ).values('vote_count',
+ 'favorited_myself',
+ 'id',
+ 'title',
+ 'author_id',
+ 'added_at',
+ 'answer_accepted',
+ 'answer_count',
+ 'comment_count',
+ 'view_count',
+ 'favourite_count',
+ 'summary',
+ 'tagnames',
+ 'vote_up_count',
+ 'vote_down_count',
+ 'last_activity_at',
+ 'la_user_id',
+ 'la_username',
+ 'la_user_gold',
+ 'la_user_silver',
+ 'la_user_bronze',
+ 'la_user_reputation')[:100]
+
+ answered_questions = Question.objects.extra(
+ select={
+ 'vote_up_count' : 'answer.vote_up_count',
+ 'vote_down_count' : 'answer.vote_down_count',
+ 'answer_id' : 'answer.id',
+ 'accepted' : 'answer.accepted',
+ 'vote_count' : 'answer.score',
+ 'comment_count' : 'answer.comment_count'
+ },
+ tables=['question', 'answer'],
+ where=['answer.deleted=False AND question.deleted=False AND answer.author_id=%s AND answer.question_id=question.id'],
+ params=[user_id],
+ order_by=['-vote_count', '-answer_id'],
+ select_params=[user_id]
+ ).distinct().values('comment_count',
+ 'id',
+ 'answer_id',
+ 'title',
+ 'author_id',
+ 'accepted',
+ 'vote_count',
+ 'answer_count',
+ 'vote_up_count',
+ 'vote_down_count')[:100]
+
+ up_votes = Vote.objects.get_up_vote_count_from_user(user)
+ down_votes = Vote.objects.get_down_vote_count_from_user(user)
+ votes_today = Vote.objects.get_votes_count_today_from_user(user)
+ votes_total = auth.VOTE_RULES['scope_votes_per_user_per_day']
+
+ question_id_set = set(map(lambda v: v['id'], list(questions))) \
+ | set(map(lambda v: v['id'], list(answered_questions)))
+
+ user_tags = Tag.objects.filter(questions__id__in = question_id_set)
+ try:
+ from django.db.models import Count
+ awards = Award.objects.extra(
+ select={'id': 'badge.id',
+ 'name':'badge.name',
+ 'description': 'badge.description',
+ 'type': 'badge.type'},
+ tables=['award', 'badge'],
+ order_by=['-awarded_at'],
+ where=['user_id=%s AND badge_id=badge.id'],
+ params=[user.id]
+ ).values('id', 'name', 'description', 'type')
+ total_awards = awards.count()
+ awards = awards.annotate(count = Count('badge__id'))
+ user_tags = user_tags.annotate(user_tag_usage_count=Count('name'))
+
+ except ImportError:
+ awards = Award.objects.extra(
+ select={'id': 'badge.id',
+ 'count': 'count(badge_id)',
+ 'name':'badge.name',
+ 'description': 'badge.description',
+ 'type': 'badge.type'},
+ tables=['award', 'badge'],
+ order_by=['-awarded_at'],
+ where=['user_id=%s AND badge_id=badge.id'],
+ params=[user.id]
+ ).values('id', 'count', 'name', 'description', 'type')
+ total_awards = awards.count()
+ awards.query.group_by = ['badge_id']
+
+ user_tags = user_tags.extra(
+ select={'user_tag_usage_count': 'COUNT(1)',},
+ order_by=['-user_tag_usage_count'],
+ )
+ user_tags.query.group_by = ['name']
+
+ if auth.can_moderate_users(request.user):
+ moderate_user_form = ModerateUserForm(instance=user)
+ else:
+ moderate_user_form = None
+
+ return render_to_response(user_view.template_file,{
+ 'moderate_user_form': moderate_user_form,
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "questions" : questions,
+ "answered_questions" : answered_questions,
+ "up_votes" : up_votes,
+ "down_votes" : down_votes,
+ "total_votes": up_votes + down_votes,
+ "votes_today_left": votes_total-votes_today,
+ "votes_total_per_day": votes_total,
+ "user_tags" : user_tags[:50],
+ "awards": awards,
+ "total_awards" : total_awards,
+ }, context_instance=RequestContext(request))
+
+def user_recent(request, user_id, user_view):
+ user = get_object_or_404(User, id=user_id)
+ def get_type_name(type_id):
+ for item in TYPE_ACTIVITY:
+ if type_id in item:
+ return item[1]
+
+ class Event:
+ def __init__(self, time, type, title, summary, answer_id, question_id):
+ self.time = time
+ self.type = get_type_name(type)
+ self.type_id = type
+ self.title = title
+ self.summary = summary
+ slug_title = slugify(title)
+ self.title_link = reverse('question', kwargs={'id':question_id}) + u'%s' % slug_title
+ if int(answer_id) > 0:
+ self.title_link += '#%s' % answer_id
+
+ class AwardEvent:
+ def __init__(self, time, type, id):
+ self.time = time
+ self.type = get_type_name(type)
+ self.type_id = type
+ self.badge = get_object_or_404(Badge, id=id)
+
+ activities = []
+ # ask questions
+ questions = Activity.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'active_at' : 'activity.active_at',
+ 'activity_type' : 'activity.activity_type'
+ },
+ tables=['activity', 'question'],
+ where=['activity.content_type_id = %s AND activity.object_id = ' +
+ 'question.id AND question.deleted=False AND activity.user_id = %s AND activity.activity_type = %s'],
+ params=[question_type_id, user_id, TYPE_ACTIVITY_ASK_QUESTION],
+ order_by=['-activity.active_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'active_at',
+ 'activity_type'
+ )
+ if len(questions) > 0:
+ questions = [(Event(q['active_at'], q['activity_type'], q['title'], '', '0', \
+ q['question_id'])) for q in questions]
+ activities.extend(questions)
+
+ # answers
+ answers = Activity.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'active_at' : 'activity.active_at',
+ 'activity_type' : 'activity.activity_type'
+ },
+ tables=['activity', 'answer', 'question'],
+ where=['activity.content_type_id = %s AND activity.object_id = answer.id AND ' +
+ 'answer.question_id=question.id AND answer.deleted=False AND activity.user_id=%s AND '+
+ 'activity.activity_type=%s AND question.deleted=False'],
+ params=[answer_type_id, user_id, TYPE_ACTIVITY_ANSWER],
+ order_by=['-activity.active_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'active_at',
+ 'activity_type'
+ )
+ if len(answers) > 0:
+ answers = [(Event(q['active_at'], q['activity_type'], q['title'], '', q['answer_id'], \
+ q['question_id'])) for q in answers]
+ activities.extend(answers)
+
+ # question comments
+ comments = Activity.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'comment.object_id',
+ 'added_at' : 'comment.added_at',
+ 'activity_type' : 'activity.activity_type'
+ },
+ tables=['activity', 'question', 'comment'],
+
+ where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+
+ 'activity.user_id = comment.user_id AND comment.object_id=question.id AND '+
+ 'comment.content_type_id=%s AND activity.user_id = %s AND activity.activity_type=%s AND ' +
+ 'question.deleted=False'],
+ params=[comment_type_id, question_type_id, user_id, TYPE_ACTIVITY_COMMENT_QUESTION],
+ order_by=['-comment.added_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'added_at',
+ 'activity_type'
+ )
+
+ if len(comments) > 0:
+ comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \
+ q['question_id'])) for q in comments]
+ activities.extend(comments)
+
+ # answer comments
+ comments = Activity.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'comment.added_at',
+ 'activity_type' : 'activity.activity_type'
+ },
+ tables=['activity', 'question', 'answer', 'comment'],
+
+ where=['activity.content_type_id = %s AND activity.object_id = comment.id AND '+
+ 'activity.user_id = comment.user_id AND comment.object_id=answer.id AND '+
+ 'comment.content_type_id=%s AND question.id = answer.question_id AND '+
+ 'activity.user_id = %s AND activity.activity_type=%s AND '+
+ 'answer.deleted=False AND question.deleted=False'],
+ params=[comment_type_id, answer_type_id, user_id, TYPE_ACTIVITY_COMMENT_ANSWER],
+ order_by=['-comment.added_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'added_at',
+ 'activity_type'
+ )
+
+ if len(comments) > 0:
+ comments = [(Event(q['added_at'], q['activity_type'], q['title'], '', q['answer_id'], \
+ q['question_id'])) for q in comments]
+ activities.extend(comments)
+
+ # question revisions
+ revisions = Activity.objects.extra(
+ select={
+ 'title' : 'question_revision.title',
+ 'question_id' : 'question_revision.question_id',
+ 'added_at' : 'activity.active_at',
+ 'activity_type' : 'activity.activity_type',
+ 'summary' : 'question_revision.summary'
+ },
+ tables=['activity', 'question_revision', 'question'],
+ where=['activity.content_type_id = %s AND activity.object_id = question_revision.id AND '+
+ 'question_revision.id=question.id AND question.deleted=False AND '+
+ 'activity.user_id = question_revision.author_id AND activity.user_id = %s AND '+
+ 'activity.activity_type=%s'],
+ params=[question_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_QUESTION],
+ order_by=['-activity.active_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'added_at',
+ 'activity_type',
+ 'summary'
+ )
+
+ if len(revisions) > 0:
+ revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], '0', \
+ q['question_id'])) for q in revisions]
+ activities.extend(revisions)
+
+ # answer revisions
+ revisions = Activity.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'activity.active_at',
+ 'activity_type' : 'activity.activity_type',
+ 'summary' : 'answer_revision.summary'
+ },
+ tables=['activity', 'answer_revision', 'question', 'answer'],
+
+ where=['activity.content_type_id = %s AND activity.object_id = answer_revision.id AND '+
+ 'activity.user_id = answer_revision.author_id AND activity.user_id = %s AND '+
+ 'answer_revision.answer_id=answer.id AND answer.question_id = question.id AND '+
+ 'question.deleted=False AND answer.deleted=False AND '+
+ 'activity.activity_type=%s'],
+ params=[answer_revision_type_id, user_id, TYPE_ACTIVITY_UPDATE_ANSWER],
+ order_by=['-activity.active_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'added_at',
+ 'answer_id',
+ 'activity_type',
+ 'summary'
+ )
+
+ if len(revisions) > 0:
+ revisions = [(Event(q['added_at'], q['activity_type'], q['title'], q['summary'], \
+ q['answer_id'], q['question_id'])) for q in revisions]
+ activities.extend(revisions)
+
+ # accepted answers
+ accept_answers = Activity.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'added_at' : 'activity.active_at',
+ 'activity_type' : 'activity.activity_type',
+ },
+ tables=['activity', 'answer', 'question'],
+ where=['activity.content_type_id = %s AND activity.object_id = answer.id AND '+
+ 'activity.user_id = question.author_id AND activity.user_id = %s AND '+
+ 'answer.deleted=False AND question.deleted=False AND '+
+ 'answer.question_id=question.id AND activity.activity_type=%s'],
+ params=[answer_type_id, user_id, TYPE_ACTIVITY_MARK_ANSWER],
+ order_by=['-activity.active_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'added_at',
+ 'activity_type',
+ )
+ if len(accept_answers) > 0:
+ accept_answers = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \
+ q['question_id'])) for q in accept_answers]
+ activities.extend(accept_answers)
+ #award history
+ awards = Activity.objects.extra(
+ select={
+ 'badge_id' : 'badge.id',
+ 'awarded_at': 'award.awarded_at',
+ 'activity_type' : 'activity.activity_type'
+ },
+ tables=['activity', 'award', 'badge'],
+ where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+
+ 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'],
+ params=[user_id, TYPE_ACTIVITY_PRIZE],
+ order_by=['-activity.active_at']
+ ).values(
+ 'badge_id',
+ 'awarded_at',
+ 'activity_type'
+ )
+ if len(awards) > 0:
+ awards = [(AwardEvent(q['awarded_at'], q['activity_type'], q['badge_id'])) for q in awards]
+ activities.extend(awards)
+
+ activities.sort(lambda x,y: cmp(y.time, x.time))
+
+ return render_to_response(user_view.template_file,{
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "activities" : activities[:user_view.data_size]
+ }, context_instance=RequestContext(request))
+
+def user_responses(request, user_id, user_view):
+ """
+ We list answers for question, comments, and answer accepted by others for this user.
+ """
+ class Response:
+ def __init__(self, type, title, question_id, answer_id, time, username, user_id, content):
+ self.type = type
+ self.title = title
+ self.titlelink = reverse('question', args=[question_id]) + u'%s#%s' % (slugify(title), answer_id)
+ self.time = time
+ self.userlink = reverse('users') + u'%s/%s/' % (user_id, username)
+ self.username = username
+ self.content = u'%s ...' % strip_tags(content)[:300]
+
+ def __unicode__(self):
+ return u'%s %s' % (self.type, self.titlelink)
+
+ user = get_object_or_404(User, id=user_id)
+ responses = []
+ answers = Answer.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'answer.added_at',
+ 'html' : 'answer.html',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ select_params=[user_id],
+ tables=['answer', 'question', 'auth_user'],
+ where=['answer.question_id = question.id AND answer.deleted=False AND question.deleted=False AND '+
+ 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'],
+ params=[user_id, user_id],
+ order_by=['-answer.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'added_at',
+ 'html',
+ 'username',
+ 'user_id'
+ )
+ if len(answers) > 0:
+ answers = [(Response(TYPE_RESPONSE['QUESTION_ANSWERED'], a['title'], a['question_id'],
+ a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
+ responses.extend(answers)
+
+
+ # question comments
+ comments = Comment.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'comment.object_id',
+ 'added_at' : 'comment.added_at',
+ 'comment' : 'comment.comment',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ tables=['question', 'auth_user', 'comment'],
+ where=['question.deleted=False AND question.author_id = %s AND comment.object_id=question.id AND '+
+ 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'],
+ params=[user_id, question_type_id, user_id],
+ order_by=['-comment.added_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'added_at',
+ 'comment',
+ 'username',
+ 'user_id'
+ )
+
+ if len(comments) > 0:
+ comments = [(Response(TYPE_RESPONSE['QUESTION_COMMENTED'], c['title'], c['question_id'],
+ '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
+ responses.extend(comments)
+
+ # answer comments
+ comments = Comment.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'comment.added_at',
+ 'comment' : 'comment.comment',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ tables=['answer', 'auth_user', 'comment', 'question'],
+ where=['answer.deleted=False AND answer.author_id = %s AND comment.object_id=answer.id AND '+
+ 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id '+
+ 'AND question.id = answer.question_id'],
+ params=[user_id, answer_type_id, user_id],
+ order_by=['-comment.added_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'added_at',
+ 'comment',
+ 'username',
+ 'user_id'
+ )
+
+ if len(comments) > 0:
+ comments = [(Response(TYPE_RESPONSE['ANSWER_COMMENTED'], c['title'], c['question_id'],
+ c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
+ responses.extend(comments)
+
+ # answer has been accepted
+ answers = Answer.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'answer.accepted_at',
+ 'html' : 'answer.html',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ select_params=[user_id],
+ tables=['answer', 'question', 'auth_user'],
+ where=['answer.question_id = question.id AND answer.deleted=False AND question.deleted=False AND '+
+ 'answer.author_id = %s AND answer.accepted=True AND question.author_id=auth_user.id'],
+ params=[user_id],
+ order_by=['-answer.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'added_at',
+ 'html',
+ 'username',
+ 'user_id'
+ )
+ if len(answers) > 0:
+ answers = [(Response(TYPE_RESPONSE['ANSWER_ACCEPTED'], a['title'], a['question_id'],
+ a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
+ responses.extend(answers)
+
+ # sort posts by time
+ responses.sort(lambda x,y: cmp(y.time, x.time))
+
+ return render_to_response(user_view.template_file,{
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "responses" : responses[:user_view.data_size],
+
+ }, context_instance=RequestContext(request))
+
+def user_votes(request, user_id, user_view):
+ user = get_object_or_404(User, id=user_id)
+ if not auth.can_view_user_votes(request.user, user):
+ raise Http404
+ votes = []
+ question_votes = Vote.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 0,
+ 'voted_at' : 'vote.voted_at',
+ 'vote' : 'vote',
+ },
+ select_params=[user_id],
+ tables=['vote', 'question', 'auth_user'],
+ where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id '+
+ 'AND vote.user_id=auth_user.id'],
+ params=[question_type_id, user_id],
+ order_by=['-vote.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'voted_at',
+ 'vote',
+ )
+ if(len(question_votes) > 0):
+ votes.extend(question_votes)
+
+ answer_votes = Vote.objects.extra(
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'voted_at' : 'vote.voted_at',
+ 'vote' : 'vote',
+ },
+ select_params=[user_id],
+ tables=['vote', 'answer', 'question', 'auth_user'],
+ where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id '+
+ 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'],
+ params=[answer_type_id, user_id],
+ order_by=['-vote.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'voted_at',
+ 'vote',
+ )
+ if(len(answer_votes) > 0):
+ votes.extend(answer_votes)
+ votes.sort(lambda x,y: cmp(y['voted_at'], x['voted_at']))
+ return render_to_response(user_view.template_file,{
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "votes" : votes[:user_view.data_size]
+
+ }, context_instance=RequestContext(request))
+
+def user_reputation(request, user_id, user_view):
+ user = get_object_or_404(User, id=user_id)
+ try:
+ from django.db.models import Sum
+ reputation = Repute.objects.extra(
+ select={'question_id':'question_id',
+ 'title': 'question.title'},
+ tables=['repute', 'question'],
+ order_by=['-reputed_at'],
+ where=['user_id=%s AND question_id=question.id'],
+ params=[user.id]
+ ).values('question_id', 'title', 'reputed_at', 'reputation')
+ reputation = reputation.annotate(positive=Sum("positive"), negative=Sum("negative"))
+ except ImportError:
+ reputation = Repute.objects.extra(
+ select={'positive':'sum(positive)', 'negative':'sum(negative)', 'question_id':'question_id',
+ 'title': 'question.title'},
+ tables=['repute', 'question'],
+ order_by=['-reputed_at'],
+ where=['user_id=%s AND question_id=question.id'],
+ params=[user.id]
+ ).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation')
+ reputation.query.group_by = ['question_id']
+
+ rep_list = []
+ for rep in Repute.objects.filter(user=user).order_by('reputed_at'):
+ dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation)
+ rep_list.append(dic)
+ reps = ','.join(rep_list)
+ reps = '[%s]' % reps
+
+ return render_to_response(user_view.template_file, {
+ "tab_name": user_view.id,
+ "tab_description": user_view.tab_description,
+ "page_title": user_view.page_title,
+ "view_user": user,
+ "reputation": reputation,
+ "reps": reps
+ }, context_instance=RequestContext(request))
+
+def user_favorites(request, user_id, user_view):
+ user = get_object_or_404(User, id=user_id)
+ questions = Question.objects.extra(
+ select={
+ 'vote_count' : 'question.vote_up_count + question.vote_down_count',
+ 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s '+
+ 'AND f.question_id = question.id',
+ 'la_user_id' : 'auth_user.id',
+ 'la_username' : 'auth_user.username',
+ 'la_user_gold' : 'auth_user.gold',
+ 'la_user_silver' : 'auth_user.silver',
+ 'la_user_bronze' : 'auth_user.bronze',
+ 'la_user_reputation' : 'auth_user.reputation'
+ },
+ select_params=[user_id],
+ tables=['question', 'auth_user', 'favorite_question'],
+ where=['question.deleted=True AND question.last_activity_by_id = auth_user.id '+
+ 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'],
+ params=[user_id],
+ order_by=['-vote_count', '-question.id']
+ ).values('vote_count',
+ 'favorited_myself',
+ 'id',
+ 'title',
+ 'author_id',
+ 'added_at',
+ 'answer_accepted',
+ 'answer_count',
+ 'comment_count',
+ 'view_count',
+ 'favourite_count',
+ 'summary',
+ 'tagnames',
+ 'vote_up_count',
+ 'vote_down_count',
+ 'last_activity_at',
+ 'la_user_id',
+ 'la_username',
+ 'la_user_gold',
+ 'la_user_silver',
+ 'la_user_bronze',
+ 'la_user_reputation')
+ return render_to_response(user_view.template_file,{
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "questions" : questions[:user_view.data_size],
+ "view_user" : user
+ }, context_instance=RequestContext(request))
+
+def user_email_subscriptions(request, user_id, user_view):
+ user = get_object_or_404(User, id=user_id)
+ if request.method == 'POST':
+ email_feeds_form = EditUserEmailFeedsForm(request.POST)
+ tag_filter_form = TagFilterSelectionForm(request.POST, instance=user)
+ if email_feeds_form.is_valid() and tag_filter_form.is_valid():
+
+ action_status = None
+ tag_filter_saved = tag_filter_form.save()
+ if tag_filter_saved:
+ action_status = _('changes saved')
+ if 'save' in request.POST:
+ feeds_saved = email_feeds_form.save(user)
+ if feeds_saved:
+ action_status = _('changes saved')
+ elif 'stop_email' in request.POST:
+ email_stopped = email_feeds_form.reset().save(user)
+ initial_values = EditUserEmailFeedsForm.NO_EMAIL_INITIAL
+ email_feeds_form = EditUserEmailFeedsForm(initial=initial_values)
+ if email_stopped:
+ action_status = _('email updates canceled')
+ else:
+ email_feeds_form = EditUserEmailFeedsForm()
+ email_feeds_form.set_initial_values(user)
+ tag_filter_form = TagFilterSelectionForm(instance=user)
+ action_status = None
+ return render_to_response(user_view.template_file,{
+ 'tab_name':user_view.id,
+ 'tab_description':user_view.tab_description,
+ 'page_title':user_view.page_title,
+ 'view_user':user,
+ 'email_feeds_form':email_feeds_form,
+ 'tag_filter_selection_form':tag_filter_form,
+ 'action_status':action_status,
+ }, context_instance=RequestContext(request))
+
+class UserView:
+ def __init__(self, id, tab_title, tab_description, page_title, view_func, template_file, data_size=0):
+ self.id = id
+ self.tab_title = tab_title
+ self.tab_description = tab_description
+ self.page_title = page_title
+ self.view_func = view_func
+ self.template_file = template_file
+ self.data_size = data_size
+
+USER_TEMPLATE_VIEWS = (
+ UserView(
+ id = 'stats',
+ tab_title = _('overview'),
+ tab_description = _('user profile'),
+ page_title = _('user profile overview'),
+ view_func = user_stats,
+ template_file = 'user_stats.html'
+ ),
+ UserView(
+ id = 'recent',
+ tab_title = _('recent activity'),
+ tab_description = _('recent user activity'),
+ page_title = _('profile - recent activity'),
+ view_func = user_recent,
+ template_file = 'user_recent.html',
+ data_size = 50
+ ),
+ UserView(
+ id = 'responses',
+ tab_title = _('responses'),
+ tab_description = _('comments and answers to others questions'),
+ page_title = _('profile - responses'),
+ view_func = user_responses,
+ template_file = 'user_responses.html',
+ data_size = 50
+ ),
+ UserView(
+ id = 'reputation',
+ tab_title = _('reputation'),
+ tab_description = _('user reputation in the community'),
+ page_title = _('profile - user reputation'),
+ view_func = user_reputation,
+ template_file = 'user_reputation.html'
+ ),
+ UserView(
+ id = 'favorites',
+ tab_title = _('favorite questions'),
+ tab_description = _('users favorite questions'),
+ page_title = _('profile - favorite questions'),
+ view_func = user_favorites,
+ template_file = 'user_favorites.html',
+ data_size = 50
+ ),
+ UserView(
+ id = 'votes',
+ tab_title = _('casted votes'),
+ tab_description = _('user vote record'),
+ page_title = _('profile - votes'),
+ view_func = user_votes,
+ template_file = 'user_votes.html',
+ data_size = 50
+ ),
+ UserView(
+ id = 'email_subscriptions',
+ tab_title = _('email subscriptions'),
+ tab_description = _('email subscription settings'),
+ page_title = _('profile - email subscriptions'),
+ view_func = user_email_subscriptions,
+ template_file = 'user_email_subscriptions.html'
+ )
+)
+
+def user(request, id):
+ sort = request.GET.get('sort', 'stats')
+ user_view = dict((v.id, v) for v in USER_TEMPLATE_VIEWS).get(sort, USER_TEMPLATE_VIEWS[0])
+ from forum.views import users
+ func = user_view.view_func
+ return func(request, id, user_view)
+
diff --git a/forum/views/writers.py b/forum/views/writers.py
new file mode 100644
index 00000000..a8f07334
--- /dev/null
+++ b/forum/views/writers.py
@@ -0,0 +1,442 @@
+# encoding:utf-8
+import os.path
+import time, datetime, random
+import logging
+from django.core.files.storage import default_storage
+from django.shortcuts import render_to_response, get_object_or_404
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
+from django.template import RequestContext
+from django.utils.html import *
+from django.utils import simplejson
+from django.utils.translation import ugettext as _
+from django.core.urlresolvers import reverse
+from django.core.exceptions import PermissionDenied
+
+from forum.utils.html import sanitize_html
+from markdown2 import Markdown
+from forum.forms import *
+from forum.models import *
+from forum.auth import *
+from forum.const import *
+from forum import auth
+from forum.utils.forms import get_next_url
+from forum.views.readers import _get_tags_cache_json
+
+# used in index page
+INDEX_PAGE_SIZE = 20
+INDEX_AWARD_SIZE = 15
+INDEX_TAGS_SIZE = 100
+# used in tags list
+DEFAULT_PAGE_SIZE = 60
+# used in questions
+QUESTIONS_PAGE_SIZE = 10
+# used in answers
+ANSWERS_PAGE_SIZE = 10
+
+markdowner = Markdown(html4tags=True)
+
+def upload(request):#ajax upload file to a question or answer
+ class FileTypeNotAllow(Exception):
+ pass
+ class FileSizeNotAllow(Exception):
+ pass
+ class UploadPermissionNotAuthorized(Exception):
+ pass
+
+ #<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>
+ xml_template = "<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url></result>"
+
+ try:
+ f = request.FILES['file-upload']
+ # check upload permission
+ if not auth.can_upload_files(request.user):
+ raise UploadPermissionNotAuthorized
+
+ # check file type
+ file_name_suffix = os.path.splitext(f.name)[1].lower()
+ if not file_name_suffix in settings.ALLOW_FILE_TYPES:
+ raise FileTypeNotAllow
+
+ # generate new file name
+ new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix
+ # use default storage to store file
+ default_storage.save(new_file_name, f)
+ # check file size
+ # byte
+ size = default_storage.size(new_file_name)
+ if size > settings.ALLOW_MAX_FILE_SIZE:
+ default_storage.delete(new_file_name)
+ raise FileSizeNotAllow
+
+ result = xml_template % ('Good', '', default_storage.url(new_file_name))
+ except UploadPermissionNotAuthorized:
+ result = xml_template % ('', _('uploading images is limited to users with >60 reputation points'), '')
+ except FileTypeNotAllow:
+ result = xml_template % ('', _("allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"), '')
+ except FileSizeNotAllow:
+ result = xml_template % ('', _("maximum upload file size is %sK") % settings.ALLOW_MAX_FILE_SIZE / 1024, '')
+ except Exception:
+ result = xml_template % ('', _('Error uploading file. Please contact the site administrator. Thank you. %s' % Exception), '')
+
+ return HttpResponse(result, mimetype="application/xml")
+
+#@login_required #actually you can post anonymously, but then must register
+def ask(request):#view used to ask a new question
+ """a view to ask a new question
+ gives space for q title, body, tags and checkbox for to post as wiki
+
+ user can start posting a question anonymously but then
+ must login/register in order for the question go be shown
+ """
+ if request.method == "POST":
+ form = AskForm(request.POST)
+ if form.is_valid():
+
+ added_at = datetime.datetime.now()
+ title = strip_tags(form.cleaned_data['title'].strip())
+ wiki = form.cleaned_data['wiki']
+ tagnames = form.cleaned_data['tags'].strip()
+ text = form.cleaned_data['text']
+ html = sanitize_html(markdowner.convert(text))
+ summary = strip_tags(html)[:120]
+
+ if request.user.is_authenticated():
+ author = request.user
+
+ question = Question.objects.create_new(
+ title = title,
+ author = author,
+ added_at = added_at,
+ wiki = wiki,
+ tagnames = tagnames,
+ summary = summary,
+ text = sanitize_html(markdowner.convert(text))
+ )
+
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ request.session.flush()
+ session_key = request.session.session_key
+ question = AnonymousQuestion(
+ session_key = session_key,
+ title = title,
+ tagnames = tagnames,
+ wiki = wiki,
+ text = text,
+ summary = summary,
+ added_at = added_at,
+ ip_addr = request.META['REMOTE_ADDR'],
+ )
+ question.save()
+ return HttpResponseRedirect(reverse('user_signin_new_question'))
+ else:
+ form = AskForm()
+
+ tags = _get_tags_cache_json()
+ return render_to_response('ask.html', {
+ 'form' : form,
+ 'tags' : tags,
+ 'email_validation_faq_url':reverse('faq') + '#validate',
+ }, context_instance=RequestContext(request))
+
+@login_required
+def edit_question(request, id):#edit or retag a question
+ """view to edit question
+ """
+ question = get_object_or_404(Question, id=id)
+ if question.deleted and not auth.can_view_deleted_post(request.user, question):
+ raise Http404
+ if auth.can_edit_post(request.user, question):
+ return _edit_question(request, question)
+ elif auth.can_retag_questions(request.user):
+ return _retag_question(request, question)
+ else:
+ raise Http404
+
+def _retag_question(request, question):#non-url subview of edit question - just retag
+ """retag question sub-view used by
+ view "edit_question"
+ """
+ if request.method == 'POST':
+ form = RetagQuestionForm(question, request.POST)
+ if form.is_valid():
+ if form.has_changed():
+ latest_revision = question.get_latest_revision()
+ retagged_at = datetime.datetime.now()
+ # Update the Question itself
+ Question.objects.filter(id=question.id).update(
+ tagnames = form.cleaned_data['tags'],
+ last_edited_at = retagged_at,
+ last_edited_by = request.user,
+ last_activity_at = retagged_at,
+ last_activity_by = request.user
+ )
+ # Update the Question's tag associations
+ tags_updated = Question.objects.update_tags(question,
+ form.cleaned_data['tags'], request.user)
+ # Create a new revision
+ QuestionRevision.objects.create(
+ question = question,
+ title = latest_revision.title,
+ author = request.user,
+ revised_at = retagged_at,
+ tagnames = form.cleaned_data['tags'],
+ summary = CONST['retagged'],
+ text = latest_revision.text
+ )
+ # send tags updated singal
+ tags_updated.send(sender=question.__class__, question=question)
+
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ form = RetagQuestionForm(question)
+ return render_to_response('question_retag.html', {
+ 'question': question,
+ 'form' : form,
+ 'tags' : _get_tags_cache_json(),
+ }, context_instance=RequestContext(request))
+
+def _edit_question(request, question):#non-url subview of edit_question - just edit the body/title
+ latest_revision = question.get_latest_revision()
+ revision_form = None
+ if request.method == 'POST':
+ if 'select_revision' in request.POST:
+ # user has changed revistion number
+ revision_form = RevisionForm(question, latest_revision, request.POST)
+ if revision_form.is_valid():
+ # Replace with those from the selected revision
+ form = EditQuestionForm(question,
+ QuestionRevision.objects.get(question=question,
+ revision=revision_form.cleaned_data['revision']))
+ else:
+ form = EditQuestionForm(question, latest_revision, request.POST)
+ else:
+ # Always check modifications against the latest revision
+ form = EditQuestionForm(question, latest_revision, request.POST)
+ if form.is_valid():
+ html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
+ if form.has_changed():
+ edited_at = datetime.datetime.now()
+ tags_changed = (latest_revision.tagnames !=
+ form.cleaned_data['tags'])
+ tags_updated = False
+ # Update the Question itself
+ updated_fields = {
+ 'title': form.cleaned_data['title'],
+ 'last_edited_at': edited_at,
+ 'last_edited_by': request.user,
+ 'last_activity_at': edited_at,
+ 'last_activity_by': request.user,
+ 'tagnames': form.cleaned_data['tags'],
+ 'summary': strip_tags(html)[:120],
+ 'html': html,
+ }
+
+ # only save when it's checked
+ # because wiki doesn't allow to be edited if last version has been enabled already
+ # and we make sure this in forms.
+ if ('wiki' in form.cleaned_data and
+ form.cleaned_data['wiki']):
+ updated_fields['wiki'] = True
+ updated_fields['wikified_at'] = edited_at
+
+ Question.objects.filter(
+ id=question.id).update(**updated_fields)
+ # Update the Question's tag associations
+ if tags_changed:
+ tags_updated = Question.objects.update_tags(
+ question, form.cleaned_data['tags'], request.user)
+ # Create a new revision
+ revision = QuestionRevision(
+ question = question,
+ title = form.cleaned_data['title'],
+ author = request.user,
+ revised_at = edited_at,
+ tagnames = form.cleaned_data['tags'],
+ text = form.cleaned_data['text'],
+ )
+ if form.cleaned_data['summary']:
+ revision.summary = form.cleaned_data['summary']
+ else:
+ revision.summary = 'No.%s Revision' % latest_revision.revision
+ revision.save()
+
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+
+ revision_form = RevisionForm(question, latest_revision)
+ form = EditQuestionForm(question, latest_revision)
+ return render_to_response('question_edit.html', {
+ 'question': question,
+ 'revision_form': revision_form,
+ 'form' : form,
+ 'tags' : _get_tags_cache_json()
+ }, context_instance=RequestContext(request))
+
+@login_required
+def edit_answer(request, id):
+ answer = get_object_or_404(Answer, id=id)
+ if answer.deleted and not auth.can_view_deleted_post(request.user, answer):
+ raise Http404
+ elif not auth.can_edit_post(request.user, answer):
+ raise Http404
+ else:
+ latest_revision = answer.get_latest_revision()
+ if request.method == "POST":
+ if 'select_revision' in request.POST:
+ # user has changed revistion number
+ revision_form = RevisionForm(answer, latest_revision, request.POST)
+ if revision_form.is_valid():
+ # Replace with those from the selected revision
+ form = EditAnswerForm(answer,
+ AnswerRevision.objects.get(answer=answer,
+ revision=revision_form.cleaned_data['revision']))
+ else:
+ form = EditAnswerForm(answer, latest_revision, request.POST)
+ else:
+ form = EditAnswerForm(answer, latest_revision, request.POST)
+ if form.is_valid():
+ html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
+ if form.has_changed():
+ edited_at = datetime.datetime.now()
+ updated_fields = {
+ 'last_edited_at': edited_at,
+ 'last_edited_by': request.user,
+ 'html': html,
+ }
+ Answer.objects.filter(id=answer.id).update(**updated_fields)
+
+ revision = AnswerRevision(
+ answer=answer,
+ author=request.user,
+ revised_at=edited_at,
+ text=form.cleaned_data['text']
+ )
+
+ if form.cleaned_data['summary']:
+ revision.summary = form.cleaned_data['summary']
+ else:
+ revision.summary = 'No.%s Revision' % latest_revision.revision
+ revision.save()
+
+ answer.question.last_activity_at = edited_at
+ answer.question.last_activity_by = request.user
+ answer.question.save()
+
+ return HttpResponseRedirect(answer.get_absolute_url())
+ else:
+ revision_form = RevisionForm(answer, latest_revision)
+ form = EditAnswerForm(answer, latest_revision)
+ return render_to_response('answer_edit.html', {
+ 'answer': answer,
+ 'revision_form': revision_form,
+ 'form': form,
+ }, context_instance=RequestContext(request))
+
+def answer(request, id):#process a new answer
+ question = get_object_or_404(Question, id=id)
+ if request.method == "POST":
+ form = AnswerForm(question, request.user, request.POST)
+ if form.is_valid():
+ wiki = form.cleaned_data['wiki']
+ text = form.cleaned_data['text']
+ update_time = datetime.datetime.now()
+
+ if request.user.is_authenticated():
+ Answer.objects.create_new(
+ question=question,
+ author=request.user,
+ added_at=update_time,
+ wiki=wiki,
+ text=sanitize_html(markdowner.convert(text)),
+ email_notify=form.cleaned_data['email_notify']
+ )
+ else:
+ request.session.flush()
+ html = sanitize_html(markdowner.convert(text))
+ summary = strip_tags(html)[:120]
+ anon = AnonymousAnswer(
+ question=question,
+ wiki=wiki,
+ text=text,
+ summary=summary,
+ session_key=request.session.session_key,
+ ip_addr=request.META['REMOTE_ADDR'],
+ )
+ anon.save()
+ return HttpResponseRedirect(reverse('user_signin_new_answer'))
+
+ return HttpResponseRedirect(question.get_absolute_url())
+
+def __generate_comments_json(obj, type, user):#non-view generates json data for the post comments
+ comments = obj.comments.all().order_by('id')
+ # {"Id":6,"PostId":38589,"CreationDate":"an hour ago","Text":"hello there!","UserDisplayName":"Jarrod Dixon","UserUrl":"/users/3/jarrod-dixon","DeleteUrl":null}
+ json_comments = []
+ from forum.templatetags.extra_tags import diff_date
+ for comment in comments:
+ comment_user = comment.user
+ delete_url = ""
+ if user != None and auth.can_delete_comment(user, comment):
+ #/posts/392845/comments/219852/delete
+ #todo translate this url
+ delete_url = reverse('index') + type + "s/%s/comments/%s/delete/" % (obj.id, comment.id)
+ json_comments.append({"id" : comment.id,
+ "object_id" : obj.id,
+ "comment_age" : diff_date(comment.added_at),
+ "text" : comment.comment,
+ "user_display_name" : comment_user.username,
+ "user_url" : comment_user.get_profile_url(),
+ "delete_url" : delete_url
+ })
+
+ data = simplejson.dumps(json_comments)
+ return HttpResponse(data, mimetype="application/json")
+
+
+def question_comments(request, id):#ajax handler for loading comments to question
+ question = get_object_or_404(Question, id=id)
+ user = request.user
+ return __comments(request, question, 'question')
+
+def answer_comments(request, id):#ajax handler for loading comments on answer
+ answer = get_object_or_404(Answer, id=id)
+ user = request.user
+ return __comments(request, answer, 'answer')
+
+def __comments(request, obj, type):#non-view generic ajax handler to load comments to an object
+ # only support get post comments by ajax now
+ user = request.user
+ if request.is_ajax():
+ if request.method == "GET":
+ response = __generate_comments_json(obj, type, user)
+ elif request.method == "POST":
+ if auth.can_add_comments(user,obj):
+ comment_data = request.POST.get('comment')
+ comment = Comment(content_object=obj, comment=comment_data, user=request.user)
+ comment.save()
+ obj.comment_count = obj.comment_count + 1
+ obj.save()
+ response = __generate_comments_json(obj, type, user)
+ else:
+ response = HttpResponseForbidden(mimetype="application/json")
+ return response
+
+def delete_comment(request, object_id='', comment_id='', commented_object_type=None):#ajax handler to delete comment
+ response = None
+ commented_object = None
+ if commented_object_type == 'question':
+ commented_object = Question
+ elif commented_object_type == 'answer':
+ commented_object = Answer
+
+ if request.is_ajax():
+ comment = get_object_or_404(Comment, id=comment_id)
+ if auth.can_delete_comment(request.user, comment):
+ obj = get_object_or_404(commented_object, id=object_id)
+ obj.comments.remove(comment)
+ obj.comment_count = obj.comment_count - 1
+ obj.save()
+ user = request.user
+ return __generate_comments_json(obj, commented_object_type, user)
+ raise PermissionDenied()
diff --git a/utils/__init__.py b/forum_modules/__init__.py
index e69de29b..e69de29b 100644..100755
--- a/utils/__init__.py
+++ b/forum_modules/__init__.py
diff --git a/forum_modules/books/__init__.py b/forum_modules/books/__init__.py
new file mode 100755
index 00000000..a182c87c
--- /dev/null
+++ b/forum_modules/books/__init__.py
@@ -0,0 +1,3 @@
+NAME = 'Osqa Books'
+DESCRIPTION = "Allows discussion around books."
+CAN_ENABLE = True
diff --git a/forum_modules/books/models.py b/forum_modules/books/models.py
new file mode 100755
index 00000000..a78c0e76
--- /dev/null
+++ b/forum_modules/books/models.py
@@ -0,0 +1,63 @@
+from django.db import models
+from django.contrib.auth.models import User
+from forum.models import Question
+from django.core.urlresolvers import reverse
+from django.utils.http import urlquote as django_urlquote
+from django.template.defaultfilters import slugify
+
+class Book(models.Model):
+ """
+ Model for book info
+ """
+ user = models.ForeignKey(User)
+ title = models.CharField(max_length=255)
+ short_name = models.CharField(max_length=255)
+ author = models.CharField(max_length=255)
+ price = models.DecimalField(max_digits=6, decimal_places=2)
+ pages = models.SmallIntegerField()
+ published_at = models.DateTimeField()
+ publication = models.CharField(max_length=255)
+ cover_img = models.CharField(max_length=255)
+ tagnames = models.CharField(max_length=125)
+ added_at = models.DateTimeField()
+ last_edited_at = models.DateTimeField()
+ questions = models.ManyToManyField(Question, related_name='book', db_table='book_question')
+
+ def get_absolute_url(self):
+ return reverse('book', args=[django_urlquote(slugify(self.short_name))])
+
+ def __unicode__(self):
+ return self.title
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'book'
+
+class BookAuthorInfo(models.Model):
+ """
+ Model for book author info
+ """
+ user = models.ForeignKey(User)
+ book = models.ForeignKey(Book)
+ blog_url = models.CharField(max_length=255)
+ added_at = models.DateTimeField()
+ last_edited_at = models.DateTimeField()
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'book_author_info'
+
+class BookAuthorRss(models.Model):
+ """
+ Model for book author blog rss
+ """
+ user = models.ForeignKey(User)
+ book = models.ForeignKey(Book)
+ title = models.CharField(max_length=255)
+ url = models.CharField(max_length=255)
+ rss_created_at = models.DateTimeField()
+ added_at = models.DateTimeField()
+
+ class Meta:
+ app_label = 'forum'
+ db_table = u'book_author_rss' \ No newline at end of file
diff --git a/forum_modules/books/urls.py b/forum_modules/books/urls.py
new file mode 100755
index 00000000..bc0811e7
--- /dev/null
+++ b/forum_modules/books/urls.py
@@ -0,0 +1,10 @@
+from django.conf.urls.defaults import *
+from django.utils.translation import ugettext as _
+
+import views as app
+
+urlpatterns = patterns('',
+ url(r'^%s$' % _('books/'), app.books, name='books'),
+ url(r'^%s%s(?P<short_name>[^/]+)/$' % (_('books/'), _('ask/')), app.ask_book, name='ask_book'),
+ url(r'^%s(?P<short_name>[^/]+)/$' % _('books/'), app.book, name='book'),
+) \ No newline at end of file
diff --git a/forum_modules/books/views.py b/forum_modules/books/views.py
new file mode 100755
index 00000000..35e9f0fe
--- /dev/null
+++ b/forum_modules/books/views.py
@@ -0,0 +1,142 @@
+from django.shortcuts import render_to_response, get_object_or_404
+from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
+from django.template import RequestContext
+from django.contrib.auth.decorators import login_required
+from django.core.urlresolvers import reverse
+from django.utils.html import *
+
+from models import *
+
+from forum.forms import AskForm
+from forum.views.readers import _get_tags_cache_json
+from forum.models import *
+from forum.utils.html import sanitize_html
+
+def books(request):
+ return HttpResponseRedirect(reverse('books') + '/mysql-zhaoyang')
+
+def book(request, short_name, unanswered=False):
+ """
+ 1. questions list
+ 2. book info
+ 3. author info and blog rss items
+ """
+ """
+ List of Questions, Tagged questions, and Unanswered questions.
+ """
+ books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
+ match_count = len(books)
+ if match_count == 0:
+ raise Http404
+ else:
+ # the book info
+ book = books[0]
+ # get author info
+ author_info = BookAuthorInfo.objects.get(book=book)
+ # get author rss info
+ author_rss = BookAuthorRss.objects.filter(book=book)
+
+ # get pagesize from session, if failed then get default value
+ user_page_size = request.session.get("pagesize", QUESTIONS_PAGE_SIZE)
+ # set pagesize equal to logon user specified value in database
+ if request.user.is_authenticated() and request.user.questions_per_page > 0:
+ user_page_size = request.user.questions_per_page
+
+ try:
+ page = int(request.GET.get('page', '1'))
+ except ValueError:
+ page = 1
+
+ view_id = request.GET.get('sort', None)
+ view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
+ try:
+ orderby = view_dic[view_id]
+ except KeyError:
+ view_id = "latest"
+ orderby = "-added_at"
+
+ # check if request is from tagged questions
+ if unanswered:
+ # check if request is from unanswered questions
+ # Article.objects.filter(publications__id__exact=1)
+ objects = Question.objects.filter(book__id__exact=book.id, deleted=False, answer_count=0).order_by(orderby)
+ else:
+ objects = Question.objects.filter(book__id__exact=book.id, deleted=False).order_by(orderby)
+
+ # RISK - inner join queries
+ objects = objects.select_related();
+ objects_list = Paginator(objects, user_page_size)
+ questions = objects_list.page(page)
+
+ return render_to_response('book.html', {
+ "book" : book,
+ "author_info" : author_info,
+ "author_rss" : author_rss,
+ "questions" : questions,
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': questions.has_previous(),
+ 'has_next': questions.has_next(),
+ 'previous': questions.previous_page_number(),
+ 'next': questions.next_page_number(),
+ 'base_url' : request.path + '?sort=%s&' % view_id,
+ 'pagesize' : user_page_size
+ }
+ }, context_instance=RequestContext(request))
+
+@login_required
+def ask_book(request, short_name):
+ if request.method == "POST":
+ form = AskForm(request.POST)
+ if form.is_valid():
+ added_at = datetime.datetime.now()
+ html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
+ question = Question(
+ title = strip_tags(form.cleaned_data['title']),
+ author = request.user,
+ added_at = added_at,
+ last_activity_at = added_at,
+ last_activity_by = request.user,
+ wiki = form.cleaned_data['wiki'],
+ tagnames = form.cleaned_data['tags'].strip(),
+ html = html,
+ summary = strip_tags(html)[:120]
+ )
+ if question.wiki:
+ question.last_edited_by = question.author
+ question.last_edited_at = added_at
+ question.wikified_at = added_at
+
+ question.save()
+
+ # create the first revision
+ QuestionRevision.objects.create(
+ question = question,
+ revision = 1,
+ title = question.title,
+ author = request.user,
+ revised_at = added_at,
+ tagnames = question.tagnames,
+ summary = CONST['default_version'],
+ text = form.cleaned_data['text']
+ )
+
+ books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
+ match_count = len(books)
+ if match_count == 1:
+ # the book info
+ book = books[0]
+ book.questions.add(question)
+
+ return HttpResponseRedirect(question.get_absolute_url())
+ else:
+ form = AskForm()
+
+ tags = _get_tags_cache_json()
+ return render_to_response('ask.html', {
+ 'form' : form,
+ 'tags' : tags,
+ 'email_validation_faq_url': reverse('faq') + '#validate',
+ }, context_instance=RequestContext(request)) \ No newline at end of file
diff --git a/forum_modules/pgfulltext/__init__.py b/forum_modules/pgfulltext/__init__.py
new file mode 100755
index 00000000..8215e1a9
--- /dev/null
+++ b/forum_modules/pgfulltext/__init__.py
@@ -0,0 +1,9 @@
+NAME = 'Postgresql Full Text Search'
+DESCRIPTION = "Enables PostgreSql full text search functionality."
+
+try:
+ import psycopg2
+ CAN_ENABLE = True
+except:
+ CAN_ENABLE = False
+ \ No newline at end of file
diff --git a/forum_modules/pgfulltext/handlers.py b/forum_modules/pgfulltext/handlers.py
new file mode 100755
index 00000000..f4a7a3b2
--- /dev/null
+++ b/forum_modules/pgfulltext/handlers.py
@@ -0,0 +1,11 @@
+from forum.models import Question
+
+def question_search(keywords, orderby):
+ return Question.objects.filter(deleted=False).extra(
+ select={
+ 'ranking': "ts_rank_cd(tsv, plainto_tsquery(%s), 32)",
+ },
+ where=["tsv @@ plainto_tsquery(%s)"],
+ params=[keywords],
+ select_params=[keywords]
+ ).order_by(orderby, '-ranking') \ No newline at end of file
diff --git a/pgfulltext/management.py b/forum_modules/pgfulltext/management.py
index 04303092..487580ff 100644..100755
--- a/pgfulltext/management.py
+++ b/forum_modules/pgfulltext/management.py
@@ -5,7 +5,7 @@ from django.conf import settings
import forum.models
-if settings.USE_PG_FTS:
+if settings.DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ):
from django.db.models.signals import post_syncdb
def setup_pgfulltext(sender, **kwargs):
@@ -15,9 +15,15 @@ if settings.USE_PG_FTS:
post_syncdb.connect(setup_pgfulltext)
def install_pg_fts():
- f = open(os.path.join(os.path.dirname(__file__), '../sql_scripts/pg_fts_install.sql'), 'r')
- cursor = connection.cursor()
- cursor.execute(f.read())
- transaction.commit_unless_managed()
+ f = open(os.path.join(os.path.dirname(__file__), 'pg_fts_install.sql'), 'r')
+
+ try:
+ cursor = connection.cursor()
+ cursor.execute(f.read())
+ transaction.commit_unless_managed()
+ except:
+ pass
+ finally:
+ cursor.close()
+
f.close()
- \ No newline at end of file
diff --git a/forum_modules/pgfulltext/pg_fts_install.sql b/forum_modules/pgfulltext/pg_fts_install.sql
new file mode 100755
index 00000000..72eca516
--- /dev/null
+++ b/forum_modules/pgfulltext/pg_fts_install.sql
@@ -0,0 +1,38 @@
+ALTER TABLE question ADD COLUMN tsv tsvector;
+
+CREATE OR REPLACE FUNCTION public.create_plpgsql_language ()
+ RETURNS TEXT
+ AS $$
+ CREATE LANGUAGE plpgsql;
+ SELECT 'language plpgsql created'::TEXT;
+ $$
+LANGUAGE 'sql';
+
+SELECT CASE WHEN
+ (SELECT true::BOOLEAN
+ FROM pg_language
+ WHERE lanname='plpgsql')
+ THEN
+ (SELECT 'language already installed'::TEXT)
+ ELSE
+ (SELECT public.create_plpgsql_language())
+ END;
+
+DROP FUNCTION public.create_plpgsql_language ();
+
+CREATE OR REPLACE FUNCTION set_question_tsv() RETURNS TRIGGER AS $$
+begin
+ new.tsv :=
+ setweight(to_tsvector('english', coalesce(new.tagnames,'')), 'A') ||
+ setweight(to_tsvector('english', coalesce(new.title,'')), 'B') ||
+ setweight(to_tsvector('english', coalesce(new.summary,'')), 'C');
+ RETURN new;
+end
+$$ LANGUAGE plpgsql;
+
+CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
+ON question FOR EACH ROW EXECUTE PROCEDURE set_question_tsv();
+
+ CREATE INDEX question_tsv ON question USING gin(tsv);
+
+UPDATE question SET title = title;
diff --git a/forum_modules/sphinxfulltext/DISABLED b/forum_modules/sphinxfulltext/DISABLED
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/forum_modules/sphinxfulltext/DISABLED
diff --git a/forum_modules/sphinxfulltext/__init__.py b/forum_modules/sphinxfulltext/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/forum_modules/sphinxfulltext/__init__.py
diff --git a/forum_modules/sphinxfulltext/dependencies.py b/forum_modules/sphinxfulltext/dependencies.py
new file mode 100755
index 00000000..046ebfc5
--- /dev/null
+++ b/forum_modules/sphinxfulltext/dependencies.py
@@ -0,0 +1,2 @@
+DJANGO_APPS = ('djangosphinx', )
+
diff --git a/forum_modules/sphinxfulltext/handlers.py b/forum_modules/sphinxfulltext/handlers.py
new file mode 100755
index 00000000..226acf72
--- /dev/null
+++ b/forum_modules/sphinxfulltext/handlers.py
@@ -0,0 +1,4 @@
+from forum.models import Question
+
+def question_search(keywords, orderby):
+ return Question.search.query(keywords) \ No newline at end of file
diff --git a/forum_modules/sphinxfulltext/models.py b/forum_modules/sphinxfulltext/models.py
new file mode 100755
index 00000000..9db4aa86
--- /dev/null
+++ b/forum_modules/sphinxfulltext/models.py
@@ -0,0 +1,10 @@
+from forum.models import Question
+from django.conf import settings
+from djangosphinx.manager import SphinxSearch
+
+
+Question.add_to_class('search', SphinxSearch(
+ index=' '.join(settings.SPHINX_SEARCH_INDICES),
+ mode='SPH_MATCH_ALL',
+ )
+ )
diff --git a/forum_modules/sphinxfulltext/settings.py b/forum_modules/sphinxfulltext/settings.py
new file mode 100755
index 00000000..7c2da124
--- /dev/null
+++ b/forum_modules/sphinxfulltext/settings.py
@@ -0,0 +1,5 @@
+SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
+SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
+#last item, especially if you have just one :)
+SPHINX_SERVER='localhost'
+SPHINX_PORT=3312
diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo
index ef3007a0..20813cb3 100644
--- a/locale/en/LC_MESSAGES/django.mo
+++ b/locale/en/LC_MESSAGES/django.mo
Binary files differ
diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po
index ee40fa36..3f53e36d 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: 2009-12-09 08:54-0800\n"
+"POT-Creation-Date: 2010-02-22 22:59-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,297 +16,228 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: django_authopenid/forms.py:70
-msgid "choose a username"
-msgstr "Choose screen name"
-
-#: django_authopenid/forms.py:76
-msgid "user name is required"
-msgstr ""
-
-#: django_authopenid/forms.py:77
-msgid "sorry, this name is taken, please choose another"
-msgstr ""
-
-#: django_authopenid/forms.py:78
-msgid "sorry, this name is not allowed, please choose another"
-msgstr ""
-
-#: django_authopenid/forms.py:79
-msgid "sorry, there is no user with this name"
-msgstr ""
-
-#: django_authopenid/forms.py:80
-msgid "sorry, we have a serious error - user name is taken by several users"
-msgstr ""
-
-#: django_authopenid/forms.py:81
-msgid "user name can only consist of letters, empty space and underscore"
-msgstr ""
-
-#: django_authopenid/forms.py:116
-msgid "your email address"
-msgstr "Your email <i>(never shared)</i>"
-
-#: django_authopenid/forms.py:117
-msgid "email address is required"
-msgstr ""
-
-#: django_authopenid/forms.py:118
-msgid "please enter a valid email address"
-msgstr ""
-
-#: django_authopenid/forms.py:119
-msgid "this email is already used by someone else, please choose another"
-msgstr ""
-
-#: django_authopenid/forms.py:163 django_authopenid/views.py:118
+#: django_authopenid/forms.py:71 django_authopenid/views.py:118
msgid "i-names are not supported"
msgstr ""
-#: django_authopenid/forms.py:219
+#: django_authopenid/forms.py:134
msgid "Account with this name already exists on the forum"
msgstr ""
-#: django_authopenid/forms.py:220
+#: django_authopenid/forms.py:135
msgid "can't have two logins to the same account yet, sorry."
msgstr ""
-#: django_authopenid/forms.py:242
+#: django_authopenid/forms.py:157
msgid "Please enter valid username and password (both are case-sensitive)."
msgstr ""
-#: django_authopenid/forms.py:245 django_authopenid/forms.py:295
+#: django_authopenid/forms.py:160 django_authopenid/forms.py:210
msgid "This account is inactive."
msgstr ""
-#: django_authopenid/forms.py:247
+#: django_authopenid/forms.py:162
msgid "Login failed."
msgstr ""
-#: django_authopenid/forms.py:249
+#: django_authopenid/forms.py:164
msgid "Please enter username and password"
msgstr ""
-#: django_authopenid/forms.py:251
+#: django_authopenid/forms.py:166
msgid "Please enter your password"
msgstr ""
-#: django_authopenid/forms.py:253
+#: django_authopenid/forms.py:168
msgid "Please enter user name"
msgstr ""
-#: django_authopenid/forms.py:291
+#: 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:313
-msgid "choose password"
-msgstr "Password"
-
-#: django_authopenid/forms.py:314
-msgid "password is required"
-msgstr ""
-
-#: django_authopenid/forms.py:317
-msgid "retype password"
-msgstr "Password <i>(please retype)</i>"
-
-#: django_authopenid/forms.py:318
-msgid "please, retype your password"
-msgstr ""
-
-#: django_authopenid/forms.py:319
-msgid "sorry, entered passwords did not match, please try again"
-msgstr ""
-
-#: django_authopenid/forms.py:344
+#: django_authopenid/forms.py:229
msgid "Current password"
msgstr ""
-#: django_authopenid/forms.py:346
-msgid "New password"
-msgstr ""
-
-#: django_authopenid/forms.py:348
-msgid "Retype new password"
-msgstr ""
-
-#: django_authopenid/forms.py:359
+#: django_authopenid/forms.py:240
msgid ""
"Old password is incorrect. Please enter the correct "
"password."
msgstr ""
-#: django_authopenid/forms.py:371
-msgid "new passwords do not match"
-msgstr ""
-
-#: django_authopenid/forms.py:435
+#: django_authopenid/forms.py:305
msgid "Your user name (<i>required</i>)"
msgstr ""
-#: django_authopenid/forms.py:450
+#: django_authopenid/forms.py:320
msgid "Incorrect username."
msgstr "sorry, there is no such user name"
-#: django_authopenid/urls.py:9 django_authopenid/urls.py:10
-#: django_authopenid/urls.py:11 django_authopenid/urls.py:13 forum/urls.py:24
+#: 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:10
+#: django_authopenid/urls.py:24 fbconnect/urls.py:13 fbconnect/urls.py:17
msgid "newquestion/"
msgstr ""
-#: django_authopenid/urls.py:11
+#: django_authopenid/urls.py:25 fbconnect/urls.py:14 fbconnect/urls.py:18
msgid "newanswer/"
msgstr ""
-#: django_authopenid/urls.py:12
+#: django_authopenid/urls.py:26
msgid "signout/"
msgstr ""
-#: django_authopenid/urls.py:13
+#: django_authopenid/urls.py:27
msgid "complete/"
msgstr ""
-#: django_authopenid/urls.py:15
-msgid "external-login/"
-msgstr ""
-
-#: django_authopenid/urls.py:16
+#: django_authopenid/urls.py:29 fbconnect/urls.py:16 fbconnect/urls.py:17
+#: fbconnect/urls.py:18
msgid "register/"
msgstr ""
-#: django_authopenid/urls.py:17
+#: django_authopenid/urls.py:30
msgid "signup/"
msgstr ""
-#: django_authopenid/urls.py:19
+#: django_authopenid/urls.py:32
msgid "sendpw/"
msgstr ""
-#: django_authopenid/urls.py:20 django_authopenid/urls.py:24
+#: django_authopenid/urls.py:33 django_authopenid/urls.py:37
msgid "password/"
msgstr ""
-#: django_authopenid/urls.py:20
+#: django_authopenid/urls.py:33
msgid "confirm/"
msgstr ""
-#: django_authopenid/urls.py:23
+#: django_authopenid/urls.py:36
msgid "account_settings"
msgstr ""
-#: django_authopenid/urls.py:25 django_authopenid/urls.py:26
-#: django_authopenid/urls.py:27 django_authopenid/urls.py:28
+#: 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:25
+#: django_authopenid/urls.py:38
msgid "validate/"
msgstr ""
-#: django_authopenid/urls.py:26
+#: django_authopenid/urls.py:39
msgid "change/"
msgstr ""
-#: django_authopenid/urls.py:27
+#: django_authopenid/urls.py:40
msgid "sendkey/"
msgstr ""
-#: django_authopenid/urls.py:28
+#: django_authopenid/urls.py:41
msgid "verify/"
msgstr ""
-#: django_authopenid/urls.py:29
+#: django_authopenid/urls.py:42
msgid "openid/"
msgstr ""
-#: django_authopenid/urls.py:30 forum/urls.py:44 forum/urls.py:48
+#: django_authopenid/urls.py:43 forum/urls.py:52 forum/urls.py:56
msgid "delete/"
msgstr ""
-#: django_authopenid/views.py:124
+#: 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:532
+#: django_authopenid/views.py:593
msgid "Welcome email subject line"
msgstr "Welcome to the Q&A forum"
-#: django_authopenid/views.py:627
+#: django_authopenid/views.py:699
msgid "Password changed."
msgstr ""
-#: django_authopenid/views.py:639 django_authopenid/views.py:645
+#: 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:666
+#: django_authopenid/views.py:738
msgid "Email verification subject line"
msgstr "Verification Email from Q&A forum"
-#: django_authopenid/views.py:752
+#: django_authopenid/views.py:829
msgid "your email was not changed"
msgstr ""
-#: django_authopenid/views.py:799 django_authopenid/views.py:951
+#: 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:803 django_authopenid/views.py:958
+#: 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:811
+#: django_authopenid/views.py:889
msgid "Email Changed."
msgstr ""
-#: django_authopenid/views.py:886
+#: django_authopenid/views.py:967
msgid "This OpenID is already associated with another account."
msgstr ""
-#: django_authopenid/views.py:891
+#: django_authopenid/views.py:972
#, python-format
msgid "OpenID %s is now associated with your account."
msgstr ""
-#: django_authopenid/views.py:961
+#: django_authopenid/views.py:1045
msgid "Account deleted."
msgstr ""
-#: django_authopenid/views.py:1004
+#: django_authopenid/views.py:1097
msgid "Request for new password"
msgstr ""
-#: django_authopenid/views.py:1017
+#: django_authopenid/views.py:1111
msgid "A new password and the activation link were sent to your email address."
msgstr ""
-#: django_authopenid/views.py:1047
+#: django_authopenid/views.py:1143
#, python-format
msgid ""
"Could not change password. Confirmation key '%s' is not "
"registered."
msgstr ""
-#: django_authopenid/views.py:1056
+#: django_authopenid/views.py:1153
msgid ""
"Can not change password. User don't exist anymore in our "
"database."
msgstr ""
-#: django_authopenid/views.py:1065
+#: django_authopenid/views.py:1163
#, python-format
msgid "Password changed for %s. You may now sign in."
msgstr ""
@@ -363,7 +294,7 @@ msgstr ""
msgid "question"
msgstr ""
-#: forum/const.py:58 templates/book.html:110
+#: forum/const.py:58 forum/skins/default/templates/book.html:110
msgid "answer"
msgstr ""
@@ -439,7 +370,7 @@ msgstr ""
msgid "[deleted]"
msgstr ""
-#: forum/const.py:87 forum/views.py:849 forum/views.py:868
+#: forum/const.py:87 forum/views/readers.py:564 forum/views/readers.py:583
msgid "initial version"
msgstr ""
@@ -452,7 +383,7 @@ msgid "exclude ignored tags"
msgstr ""
#: forum/const.py:92
-msgid "allow only interesting tags"
+msgid "allow only selected tags"
msgstr ""
#: forum/feed.py:18
@@ -463,515 +394,319 @@ msgstr ""
msgid "latest questions"
msgstr ""
-#: forum/feed.py:19 forum/urls.py:52
-msgid "question/"
-msgstr ""
-
-#: forum/forms.py:16 templates/answer_edit_tips.html:35
-#: templates/answer_edit_tips.html.py:39 templates/question_edit_tips.html:32
-#: templates/question_edit_tips.html:37
+#: forum/forms.py:19 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:17
+#: forum/forms.py:20
msgid "please enter a descriptive title for your question"
msgstr ""
-#: forum/forms.py:22
+#: forum/forms.py:25
msgid "title must be > 10 characters"
msgstr ""
-#: forum/forms.py:31
+#: forum/forms.py:34
msgid "content"
msgstr ""
-#: forum/forms.py:37
+#: forum/forms.py:40
msgid "question content must be > 10 characters"
msgstr ""
-#: forum/forms.py:47 templates/header.html:28 templates/header.html.py:62
+#: forum/forms.py:50 forum/skins/default/templates/header.html:28
+#: forum/skins/default/templates/header.html:56
msgid "tags"
msgstr ""
-#: forum/forms.py:49
+#: forum/forms.py:52
msgid ""
"Tags are short keywords, with no spaces within. Up to five tags can be used."
msgstr ""
-#: forum/forms.py:56 templates/question_retag.html:39
+#: forum/forms.py:59 forum/skins/default/templates/question_retag.html:39
msgid "tags are required"
msgstr ""
-#: forum/forms.py:62
+#: forum/forms.py:65
msgid "please use 5 tags or less"
msgstr ""
-#: forum/forms.py:65
+#: forum/forms.py:68
msgid "tags must be shorter than 20 characters"
msgstr ""
-#: forum/forms.py:69
+#: forum/forms.py:72
msgid ""
"please use following characters in tags: letters 'a-z', numbers, and "
"characters '.-_#'"
msgstr ""
-#: forum/forms.py:79 templates/index.html:61 templates/index.html.py:73
-#: templates/post_contributor_info.html:7
-#: templates/question_summary_list_roll.html:26
-#: templates/question_summary_list_roll.html:38 templates/questions.html:92
-#: templates/questions.html.py:104 templates/unanswered.html:51
-#: templates/unanswered.html.py:63
+#: forum/forms.py:82
+#: 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
+#: forum/skins/default/templates/questions.html:92
+#: forum/skins/default/templates/questions.html:104
msgid "community wiki"
msgstr ""
-#: forum/forms.py:80
+#: forum/forms.py:83
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:96
+#: forum/forms.py:99
msgid "update summary:"
msgstr ""
-#: forum/forms.py:97
+#: forum/forms.py:100
msgid ""
"enter a brief summary of your revision (e.g. fixed spelling, grammar, "
"improved style, this field is optional)"
msgstr ""
-#: forum/forms.py:100
+#: forum/forms.py:103
msgid "Automatically accept user's contributions for the email updates"
msgstr ""
-#: forum/forms.py:113
+#: forum/forms.py:119
msgid "Your name:"
msgstr ""
-#: forum/forms.py:114
+#: forum/forms.py:120
msgid "Email (not shared with anyone):"
msgstr ""
-#: forum/forms.py:115
+#: forum/forms.py:121
msgid "Your message:"
msgstr ""
-#: forum/forms.py:197
+#: forum/forms.py:203
msgid "this email does not have to be linked to gravatar"
msgstr ""
-#: forum/forms.py:198
+#: forum/forms.py:205
msgid "Screen name"
msgstr ""
-#: forum/forms.py:199
+#: forum/forms.py:206
msgid "Real name"
msgstr ""
-#: forum/forms.py:200
+#: forum/forms.py:207
msgid "Website"
msgstr ""
-#: forum/forms.py:201
+#: forum/forms.py:208
msgid "Location"
msgstr ""
-#: forum/forms.py:202
+#: forum/forms.py:209
msgid "Date of birth"
msgstr ""
-#: forum/forms.py:202
+#: forum/forms.py:209
msgid "will not be shown, used to calculate age, format: YYYY-MM-DD"
msgstr ""
-#: forum/forms.py:203 templates/authopenid/settings.html:21
+#: forum/forms.py:210
+#: forum/skins/default/templates/authopenid/settings.html:21
msgid "Profile"
msgstr ""
-#: forum/forms.py:231 forum/forms.py:232
+#: forum/forms.py:241 forum/forms.py:242
msgid "this email has already been registered, please use another one"
msgstr ""
-#: forum/forms.py:238
+#: forum/forms.py:248
msgid "Choose email tag filter"
msgstr ""
-#: forum/forms.py:245 forum/forms.py:246
+#: forum/forms.py:263 forum/forms.py:264
msgid "weekly"
msgstr ""
-#: forum/forms.py:245 forum/forms.py:246
+#: forum/forms.py:263 forum/forms.py:264
msgid "no email"
msgstr ""
-#: forum/forms.py:246
+#: forum/forms.py:264
msgid "daily"
msgstr ""
-#: forum/forms.py:261
+#: forum/forms.py:279
msgid "Asked by me"
msgstr ""
-#: forum/forms.py:264
+#: forum/forms.py:282
msgid "Answered by me"
msgstr ""
-#: forum/forms.py:267
+#: forum/forms.py:285
msgid "Individually selected"
msgstr ""
-#: forum/forms.py:270
+#: forum/forms.py:288
msgid "Entire forum (tag filtered)"
msgstr ""
-#: forum/models.py:51
-msgid "Entire forum"
-msgstr ""
-
-#: forum/models.py:52
-msgid "Questions that I asked"
-msgstr ""
-
-#: forum/models.py:53
-msgid "Questions that I answered"
+#: forum/forms.py:342
+msgid "okay, let's try!"
msgstr ""
-#: forum/models.py:54
-msgid "Individually selected questions"
-msgstr ""
-
-#: forum/models.py:57
-msgid "Weekly"
+#: forum/forms.py:343
+msgid "no OSQA community email please, thanks"
msgstr ""
-#: forum/models.py:58
-msgid "Daily"
-msgstr ""
-
-#: forum/models.py:59
-msgid "No email"
+#: forum/forms.py:346
+msgid "please choose one of the options above"
msgstr ""
-#: forum/models.py:301
-#, python-format
-msgid "%(author)s modified the question"
-msgstr ""
-
-#: forum/models.py:305
-#, python-format
-msgid "%(people)s posted %(new_answer_count)s new answers"
-msgstr ""
-
-#: forum/models.py:310
-#, python-format
-msgid "%(people)s commented the question"
-msgstr ""
-
-#: forum/models.py:315
-#, python-format
-msgid "%(people)s commented answers"
-msgstr ""
-
-#: forum/models.py:317
-#, python-format
-msgid "%(people)s commented an answer"
-msgstr ""
-
-#: forum/models.py:348
-msgid "interesting"
-msgstr ""
-
-#: forum/models.py:348
-msgid "ignored"
-msgstr ""
-
-#: forum/models.py:511 templates/badges.html:53
-msgid "gold"
-msgstr ""
-
-#: forum/models.py:512 templates/badges.html:61
-msgid "silver"
-msgstr ""
-
-#: forum/models.py:513 templates/badges.html:68
-msgid "bronze"
-msgstr ""
-
-#: forum/urls.py:21
+#: forum/urls.py:28
msgid "upfiles/"
msgstr ""
-#: forum/urls.py:25
+#: forum/urls.py:33
msgid "about/"
msgstr ""
-#: forum/urls.py:26
+#: forum/urls.py:34
msgid "faq/"
msgstr ""
-#: forum/urls.py:27
+#: forum/urls.py:35
msgid "privacy/"
msgstr ""
-#: forum/urls.py:28
+#: forum/urls.py:36
msgid "logout/"
msgstr ""
-#: forum/urls.py:29 forum/urls.py:30 forum/urls.py:31 forum/urls.py:48
+#: forum/urls.py:37 forum/urls.py:38 forum/urls.py:39 forum/urls.py:56
msgid "answers/"
msgstr ""
-#: forum/urls.py:29 forum/urls.py:41 forum/urls.py:44 forum/urls.py:48
+#: forum/urls.py:37 forum/urls.py:49 forum/urls.py:52 forum/urls.py:56
msgid "comments/"
msgstr ""
-#: forum/urls.py:30 forum/urls.py:35 forum/urls.py:70
-#: templates/user_info.html:45
+#: forum/urls.py:38 forum/urls.py:43 forum/urls.py:78
+#: forum/skins/default/templates/user_info.html:45
msgid "edit/"
msgstr ""
-#: forum/urls.py:31 forum/urls.py:40
+#: forum/urls.py:39 forum/urls.py:48
msgid "revisions/"
msgstr ""
-#: forum/urls.py:32 forum/urls.py:33 forum/urls.py:34 forum/urls.py:35
-#: forum/urls.py:36 forum/urls.py:37 forum/urls.py:38 forum/urls.py:39
-#: forum/urls.py:40 forum/urls.py:41 forum/urls.py:44
+#: forum/urls.py:40 forum/urls.py:41 forum/urls.py:42 forum/urls.py:43
+#: forum/urls.py:44 forum/urls.py:45 forum/urls.py:46 forum/urls.py:47
+#: forum/urls.py:48 forum/urls.py:49 forum/urls.py:52
msgid "questions/"
msgstr ""
-#: forum/urls.py:33 forum/urls.py:80
+#: forum/urls.py:41 forum_modules/books/urls.py:8
msgid "ask/"
msgstr ""
-#: forum/urls.py:34
+#: forum/urls.py:42
msgid "unanswered/"
msgstr ""
-#: forum/urls.py:36
+#: forum/urls.py:44
msgid "close/"
msgstr ""
-#: forum/urls.py:37
+#: forum/urls.py:45
msgid "reopen/"
msgstr ""
-#: forum/urls.py:38
+#: forum/urls.py:46
msgid "answer/"
msgstr ""
-#: forum/urls.py:39
+#: forum/urls.py:47
msgid "vote/"
msgstr ""
-#: forum/urls.py:42
+#: forum/urls.py:50
msgid "command/"
msgstr ""
-#: forum/urls.py:53 forum/urls.py:54
+#: forum/urls.py:60 forum/views/readers.py:435
+msgid "question/"
+msgstr ""
+
+#: forum/urls.py:61 forum/urls.py:62
msgid "tags/"
msgstr ""
-#: forum/urls.py:56 forum/urls.py:60
+#: forum/urls.py:64 forum/urls.py:68
msgid "mark-tag/"
msgstr ""
-#: forum/urls.py:56
+#: forum/urls.py:64
msgid "interesting/"
msgstr ""
-#: forum/urls.py:60
+#: forum/urls.py:68
msgid "ignored/"
msgstr ""
-#: forum/urls.py:64
+#: forum/urls.py:72
msgid "unmark-tag/"
msgstr ""
-#: forum/urls.py:68 forum/urls.py:70 forum/urls.py:71
+#: forum/urls.py:76 forum/urls.py:78 forum/urls.py:79
msgid "users/"
msgstr ""
-#: forum/urls.py:69
+#: forum/urls.py:77
msgid "moderate-user/"
msgstr ""
-#: forum/urls.py:72 forum/urls.py:73
+#: forum/urls.py:80 forum/urls.py:81
msgid "badges/"
msgstr ""
-#: forum/urls.py:74
+#: forum/urls.py:82
msgid "messages/"
msgstr ""
-#: forum/urls.py:74
+#: forum/urls.py:82
msgid "markread/"
msgstr ""
-#: forum/urls.py:76
+#: forum/urls.py:84
msgid "nimda/"
msgstr ""
-#: forum/urls.py:78
+#: forum/urls.py:86
msgid "upload/"
msgstr ""
-#: forum/urls.py:79 forum/urls.py:80 forum/urls.py:81
-msgid "books/"
-msgstr ""
-
-#: forum/urls.py:82
+#: forum/urls.py:87
msgid "search/"
msgstr ""
-#: forum/urls.py:83
+#: forum/urls.py:88
msgid "feedback/"
msgstr ""
-#: forum/urls.py:84
+#: forum/urls.py:89 forum/urls.py:90
msgid "account/"
msgstr ""
-#: forum/user.py:16 templates/user_tabs.html:7
-msgid "overview"
-msgstr ""
-
-#: forum/user.py:17
-msgid "user profile"
-msgstr ""
-
-#: forum/user.py:18
-msgid "user profile overview"
-msgstr ""
-
-#: forum/user.py:24 templates/user_tabs.html:9
-msgid "recent activity"
-msgstr ""
-
-#: forum/user.py:25
-msgid "recent user activity"
-msgstr ""
-
-#: forum/user.py:26
-msgid "profile - recent activity"
-msgstr ""
-
-#: forum/user.py:33 templates/user_tabs.html:13
-msgid "responses"
-msgstr ""
-
-#: forum/user.py:34 templates/user_tabs.html:12
-msgid "comments and answers to others questions"
-msgstr ""
-
-#: forum/user.py:35
-msgid "profile - responses"
-msgstr ""
-
-#: forum/user.py:42 templates/user_info.html:22 templates/users.html:26
-msgid "reputation"
-msgstr "karma"
-
-#: forum/user.py:43
-msgid "user reputation in the community"
-msgstr "user karma"
-
-#: forum/user.py:44
-msgid "profile - user reputation"
-msgstr "Profile - User's Karma"
-
-#: forum/user.py:50
-msgid "favorite questions"
-msgstr ""
-
-#: forum/user.py:51
-msgid "users favorite questions"
-msgstr ""
-
-#: forum/user.py:52
-msgid "profile - favorite questions"
-msgstr ""
-
-#: forum/user.py:59 templates/user_tabs.html:20
-msgid "casted votes"
-msgstr "votes"
-
-#: forum/user.py:60 templates/user_tabs.html:20
-msgid "user vote record"
-msgstr ""
-
-#: forum/user.py:61
-msgid "profile - votes"
-msgstr ""
-
-#: forum/user.py:68 templates/user_tabs.html:28
-msgid "email subscriptions"
-msgstr ""
-
-#: forum/user.py:69 templates/user_tabs.html:27
-msgid "email subscription settings"
-msgstr ""
-
-#: forum/user.py:70
-msgid "profile - email subscriptions"
-msgstr ""
-
-#: forum/views.py:141
-msgid "Q&A forum feedback"
-msgstr ""
-
-#: forum/views.py:142
-msgid "Thanks for the feedback!"
-msgstr ""
-
-#: forum/views.py:150
-msgid "We look forward to hearing your feedback! Please, give it next time :)"
-msgstr ""
-
-#: forum/views.py:1150
-#, python-format
-msgid "subscription saved, %(email)s needs validation, see %(details_url)s"
-msgstr ""
-"Your subscription is saved, but email address %(email)s needs to be "
-"validated, please see <a href='%(details_url)s'>more details here</a>"
-
-#: forum/views.py:1158
-msgid "email update frequency has been set to daily"
-msgstr ""
-
-#: forum/views.py:2032
-msgid "changes saved"
-msgstr ""
-
-#: forum/views.py:2038
-msgid "email updates canceled"
-msgstr ""
-
-#: forum/views.py:2207
-msgid "uploading images is limited to users with >60 reputation points"
-msgstr "sorry, file uploading requires karma >60"
-
-#: forum/views.py:2209
-msgid "allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"
-msgstr ""
-
-#: forum/views.py:2211
-#, python-format
-msgid "maximum upload file size is %sK"
-msgstr ""
-
-#: forum/views.py:2213
-#, python-format
-msgid ""
-"Error uploading file. Please contact the site administrator. Thank you. %s"
-msgstr ""
-
-#: forum/management/commands/send_email_alerts.py:160
+#: forum/management/commands/send_email_alerts.py:155
msgid "email update message subject"
msgstr "news from Q&A forum"
-#: forum/management/commands/send_email_alerts.py:161
+#: forum/management/commands/send_email_alerts.py:157
#, 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"
@@ -982,11 +717,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:172
+#: forum/management/commands/send_email_alerts.py:168
msgid "new question"
msgstr ""
-#: forum/management/commands/send_email_alerts.py:182
+#: forum/management/commands/send_email_alerts.py:178
#, python-format
msgid "There is also one question which was recently "
msgid_plural ""
@@ -994,12 +729,12 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: forum/management/commands/send_email_alerts.py:187
+#: forum/management/commands/send_email_alerts.py:183
msgid ""
"Perhaps you could look up previously sent forum reminders in your mailbox."
msgstr ""
-#: forum/management/commands/send_email_alerts.py:191
+#: forum/management/commands/send_email_alerts.py:187
#, python-format
msgid ""
"go to %(link)s to change frequency of email updates or %(email)s "
@@ -1011,267 +746,305 @@ msgstr ""
"administrator at %(email)s.</p><p>Sincerely,</p><p>Your friendly Q&A forum "
"server.</p>"
-#: forum/templatetags/extra_tags.py:163 forum/templatetags/extra_tags.py:192
-#: templates/header.html:33
-msgid "badges"
+#: forum/middleware/anon_user.py:33
+#, python-format
+msgid "first time greeting with %(url)s"
msgstr ""
-#: forum/templatetags/extra_tags.py:164 forum/templatetags/extra_tags.py:191
-msgid "reputation points"
-msgstr "karma"
-
-#: forum/templatetags/extra_tags.py:247
-msgid "%b %d at %H:%M"
+#: forum/models/question.py:247
+#, python-format
+msgid "%(author)s modified the question"
msgstr ""
-#: forum/templatetags/extra_tags.py:249
-msgid "%b %d '%y at %H:%M"
+#: forum/models/question.py:251
+#, python-format
+msgid "%(people)s posted %(new_answer_count)s new answers"
msgstr ""
-#: forum/templatetags/extra_tags.py:251
-msgid "2 days ago"
+#: forum/models/question.py:256
+#, python-format
+msgid "%(people)s commented the question"
msgstr ""
-#: forum/templatetags/extra_tags.py:253
-msgid "yesterday"
+#: forum/models/question.py:261
+#, python-format
+msgid "%(people)s commented answers"
msgstr ""
-#: forum/templatetags/extra_tags.py:255
+#: forum/models/question.py:263
#, python-format
-msgid "%(hr)d hour ago"
-msgid_plural "%(hr)d hours ago"
-msgstr[0] ""
-msgstr[1] ""
+msgid "%(people)s commented an answer"
+msgstr ""
-#: forum/templatetags/extra_tags.py:257
-#, python-format
-msgid "%(min)d min ago"
-msgid_plural "%(min)d mins ago"
-msgstr[0] ""
-msgstr[1] ""
+#: forum/models/repute.py:11 forum/skins/default/templates/badges.html:53
+msgid "gold"
+msgstr ""
-#: middleware/anon_user.py:33
-#, python-format
-msgid "first time greeting with %(url)s"
+#: forum/models/repute.py:12 forum/skins/default/templates/badges.html:61
+msgid "silver"
+msgstr ""
+
+#: forum/models/repute.py:13 forum/skins/default/templates/badges.html:68
+msgid "bronze"
+msgstr ""
+
+#: forum/models/tag.py:79
+msgid "interesting"
+msgstr ""
+
+#: forum/models/tag.py:79
+msgid "ignored"
+msgstr ""
+
+#: forum/models/user.py:31
+msgid "Entire forum"
+msgstr ""
+
+#: forum/models/user.py:32
+msgid "Questions that I asked"
+msgstr ""
+
+#: forum/models/user.py:33
+msgid "Questions that I answered"
+msgstr ""
+
+#: forum/models/user.py:34
+msgid "Individually selected questions"
+msgstr ""
+
+#: forum/models/user.py:37
+msgid "Weekly"
+msgstr ""
+
+#: forum/models/user.py:38
+msgid "Daily"
+msgstr ""
+
+#: forum/models/user.py:39
+msgid "No email"
msgstr ""
-#: templates/404.html:24
+#: forum/skins/default/templates/404.html:24
msgid "Sorry, could not find the page you requested."
msgstr ""
-#: templates/404.html:26
+#: forum/skins/default/templates/404.html:26
msgid "This might have happened for the following reasons:"
msgstr ""
-#: templates/404.html:28
+#: forum/skins/default/templates/404.html:28
msgid "this question or answer has been deleted;"
msgstr ""
-#: templates/404.html:29
+#: forum/skins/default/templates/404.html:29
msgid "url has error - please check it;"
msgstr ""
-#: templates/404.html:30
+#: forum/skins/default/templates/404.html:30
msgid ""
"the page you tried to visit is protected or you don't have sufficient "
"points, see"
msgstr ""
-#: templates/404.html:31
+#: forum/skins/default/templates/404.html:31
msgid "if you believe this error 404 should not have occured, please"
msgstr ""
-#: templates/404.html:32
+#: forum/skins/default/templates/404.html:32
msgid "report this problem"
msgstr ""
-#: templates/404.html:41 templates/500.html:27
+#: forum/skins/default/templates/404.html:41
+#: forum/skins/default/templates/500.html:27
msgid "back to previous page"
msgstr ""
-#: templates/404.html:42
+#: forum/skins/default/templates/404.html:42
msgid "see all questions"
msgstr ""
-#: templates/404.html:43
+#: forum/skins/default/templates/404.html:43
msgid "see all tags"
msgstr ""
-#: templates/500.html:22
+#: forum/skins/default/templates/500.html:22
msgid "sorry, system error"
msgstr ""
-#: templates/500.html:24
+#: forum/skins/default/templates/500.html:24
msgid "system error log is recorded, error will be fixed as soon as possible"
msgstr ""
-#: templates/500.html:25
+#: forum/skins/default/templates/500.html:25
msgid "please report the error to the site administrators if you wish"
msgstr ""
-#: templates/500.html:28
+#: forum/skins/default/templates/500.html:28
msgid "see latest questions"
msgstr ""
-#: templates/500.html:29
+#: forum/skins/default/templates/500.html:29
msgid "see tags"
msgstr ""
-#: templates/about.html:6 templates/about.html.py:11
+#: forum/skins/default/templates/about.html:6
+#: forum/skins/default/templates/about.html:11
msgid "About"
msgstr ""
-#: templates/about.html:21
-msgid ""
-"<strong>CNPROG <span class=\"orange\">Q&amp;A</span></strong> is a "
-"collaboratively edited question\n"
-" and answer site created for the <strong>CNPROG</strong> "
-"community.\n"
-" "
-msgstr ""
-
-#: templates/about.html:25
-msgid ""
-"Here you can <strong>ask</strong> and <strong>answer</strong> questions, "
-"<strong>comment</strong>\n"
-" and <strong>vote</strong> for the questions of others and their answers. "
-"Both questions and answers\n"
-" <strong>can be revised</strong> and improved. Questions can be "
-"<strong>tagged</strong> with\n"
-" the relevant keywords to simplify future access and organize the "
-"accumulated material."
-msgstr ""
-
-#: templates/about.html:31
-msgid ""
-"This <span class=\"orange\">Q&amp;A</span> site is moderated by its members, "
-"hopefully - including yourself!\n"
-" Moderation rights are gradually assigned to the site users based on the "
-"accumulated <strong>\"reputation\"</strong>\n"
-" points. These points are added to the users account when others vote for "
-"his/her questions or answers.\n"
-" These points (very) roughly reflect the level of trust of the community."
-msgstr ""
-
-#: templates/answer_edit.html:5 templates/answer_edit.html.py:48
+#: forum/skins/default/templates/answer_edit.html:5
+#: forum/skins/default/templates/answer_edit.html:48
msgid "Edit answer"
msgstr ""
-#: templates/answer_edit.html:25 templates/answer_edit.html.py:28
-#: templates/ask.html:26 templates/ask.html.py:29 templates/question.html:45
-#: templates/question.html.py:48 templates/question_edit.html:25
-#: templates/question_edit.html.py:28
+#: forum/skins/default/templates/answer_edit.html:25
+#: forum/skins/default/templates/answer_edit.html:28
+#: forum/skins/default/templates/ask.html:26
+#: forum/skins/default/templates/ask.html:29
+#: forum/skins/default/templates/question.html:45
+#: forum/skins/default/templates/question.html:48
+#: forum/skins/default/templates/question_edit.html:25
+#: forum/skins/default/templates/question_edit.html:28
msgid "hide preview"
msgstr ""
-#: templates/answer_edit.html:28 templates/ask.html:29
-#: templates/question.html:48 templates/question_edit.html:28
+#: forum/skins/default/templates/answer_edit.html:28
+#: forum/skins/default/templates/ask.html:29
+#: forum/skins/default/templates/question.html:48
+#: forum/skins/default/templates/question_edit.html:28
msgid "show preview"
msgstr ""
-#: templates/answer_edit.html:48 templates/question_edit.html:66
-#: templates/question_retag.html:53 templates/revisions_answer.html:38
-#: templates/revisions_question.html:38
+#: forum/skins/default/templates/answer_edit.html:48
+#: forum/skins/default/templates/question_edit.html:66
+#: forum/skins/default/templates/question_retag.html:53
+#: forum/skins/default/templates/revisions_answer.html:38
+#: forum/skins/default/templates/revisions_question.html:38
msgid "back"
msgstr ""
-#: templates/answer_edit.html:53 templates/question_edit.html:71
-#: templates/revisions_answer.html:52 templates/revisions_question.html:52
+#: forum/skins/default/templates/answer_edit.html:53
+#: forum/skins/default/templates/question_edit.html:71
+#: forum/skins/default/templates/revisions_answer.html:52
+#: forum/skins/default/templates/revisions_question.html:52
msgid "revision"
msgstr ""
-#: templates/answer_edit.html:56 templates/question_edit.html:75
+#: forum/skins/default/templates/answer_edit.html:56
+#: forum/skins/default/templates/question_edit.html:75
msgid "select revision"
msgstr ""
-#: templates/answer_edit.html:63 templates/ask.html:97
-#: templates/question.html:434 templates/question_edit.html:92
+#: forum/skins/default/templates/answer_edit.html:63
+#: forum/skins/default/templates/ask.html:97
+#: forum/skins/default/templates/question.html:432
+#: forum/skins/default/templates/question_edit.html:92
msgid "Toggle the real time Markdown editor preview"
msgstr ""
-#: templates/answer_edit.html:63 templates/ask.html:97
-#: templates/question.html:435 templates/question_edit.html:92
+#: forum/skins/default/templates/answer_edit.html:63
+#: forum/skins/default/templates/ask.html:97
+#: forum/skins/default/templates/question.html:433
+#: forum/skins/default/templates/question_edit.html:92
msgid "toggle preview"
msgstr ""
-#: templates/answer_edit.html:72 templates/question_edit.html:118
-#: templates/question_retag.html:74
+#: forum/skins/default/templates/answer_edit.html:72
+#: forum/skins/default/templates/question_edit.html:118
+#: forum/skins/default/templates/question_retag.html:74
msgid "Save edit"
msgstr ""
-#: templates/answer_edit.html:73 templates/close.html:29
-#: templates/feedback.html:50 templates/question_edit.html:119
-#: templates/question_retag.html:75 templates/reopen.html:30
-#: templates/user_edit.html:87 templates/authopenid/changeemail.html:40
+#: forum/skins/default/templates/answer_edit.html:73
+#: forum/skins/default/templates/close.html:29
+#: forum/skins/default/templates/feedback.html:50
+#: forum/skins/default/templates/question_edit.html:119
+#: forum/skins/default/templates/question_retag.html:75
+#: forum/skins/default/templates/reopen.html:30
+#: forum/skins/default/templates/user_edit.html:87
+#: forum/skins/default/templates/authopenid/changeemail.html:40
msgid "Cancel"
msgstr ""
-#: templates/answer_edit_tips.html:4
+#: forum/skins/default/templates/answer_edit_tips.html:4
msgid "answer tips"
msgstr "Tips"
-#: templates/answer_edit_tips.html:7
+#: forum/skins/default/templates/answer_edit_tips.html:7
msgid "please make your answer relevant to this community"
msgstr ""
-#: templates/answer_edit_tips.html:10
+#: forum/skins/default/templates/answer_edit_tips.html:10
msgid "try to give an answer, rather than engage into a discussion"
msgstr ""
-#: templates/answer_edit_tips.html:13
+#: forum/skins/default/templates/answer_edit_tips.html:13
msgid "please try to provide details"
msgstr ""
-#: templates/answer_edit_tips.html:16 templates/question_edit_tips.html:13
+#: forum/skins/default/templates/answer_edit_tips.html:16
+#: forum/skins/default/templates/question_edit_tips.html:13
msgid "be clear and concise"
msgstr ""
-#: templates/answer_edit_tips.html:20 templates/question_edit_tips.html:17
+#: forum/skins/default/templates/answer_edit_tips.html:20
+#: forum/skins/default/templates/question_edit_tips.html:17
msgid "see frequently asked questions"
msgstr ""
-#: templates/answer_edit_tips.html:26 templates/question_edit_tips.html:23
+#: forum/skins/default/templates/answer_edit_tips.html:26
+#: forum/skins/default/templates/question_edit_tips.html:23
msgid "Markdown tips"
msgstr "Markdown basics"
-#: templates/answer_edit_tips.html:29 templates/question_edit_tips.html:26
+#: forum/skins/default/templates/answer_edit_tips.html:29
+#: forum/skins/default/templates/question_edit_tips.html:26
msgid "*italic* or __italic__"
msgstr ""
-#: templates/answer_edit_tips.html:32 templates/question_edit_tips.html:29
+#: forum/skins/default/templates/answer_edit_tips.html:32
+#: forum/skins/default/templates/question_edit_tips.html:29
msgid "**bold** or __bold__"
msgstr ""
-#: templates/answer_edit_tips.html:35 templates/question_edit_tips.html:32
+#: forum/skins/default/templates/answer_edit_tips.html:35
+#: forum/skins/default/templates/question_edit_tips.html:32
msgid "link"
msgstr ""
-#: templates/answer_edit_tips.html:35 templates/answer_edit_tips.html.py:39
-#: templates/question_edit_tips.html:32 templates/question_edit_tips.html:37
+#: 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 "text"
msgstr ""
-#: templates/answer_edit_tips.html:39 templates/question_edit_tips.html:37
+#: forum/skins/default/templates/answer_edit_tips.html:39
+#: forum/skins/default/templates/question_edit_tips.html:37
msgid "image"
msgstr ""
-#: templates/answer_edit_tips.html:43 templates/question_edit_tips.html:41
+#: forum/skins/default/templates/answer_edit_tips.html:43
+#: forum/skins/default/templates/question_edit_tips.html:41
msgid "numbered list:"
msgstr ""
-#: templates/answer_edit_tips.html:48 templates/question_edit_tips.html:46
+#: forum/skins/default/templates/answer_edit_tips.html:48
+#: forum/skins/default/templates/question_edit_tips.html:46
msgid "basic HTML tags are also supported"
msgstr ""
-#: templates/answer_edit_tips.html:52 templates/question_edit_tips.html:50
+#: forum/skins/default/templates/answer_edit_tips.html:52
+#: forum/skins/default/templates/question_edit_tips.html:50
msgid "learn more about Markdown"
msgstr ""
-#: templates/ask.html:5 templates/ask.html.py:61
+#: forum/skins/default/templates/ask.html:5
+#: forum/skins/default/templates/ask.html:61
msgid "Ask a question"
msgstr ""
-#: templates/ask.html:68
+#: forum/skins/default/templates/ask.html:68
msgid "login to post question info"
msgstr ""
"<span class=\"strong big\">You are welcome to start submitting your question "
@@ -1280,7 +1053,7 @@ msgstr ""
"will be published after you log in. Login/signup process is very simple. "
"Login takes about 30 seconds, initial signup takes a minute or less."
-#: templates/ask.html:74
+#: forum/skins/default/templates/ask.html:74
#, python-format
msgid ""
"must have valid %(email)s to post, \n"
@@ -1293,41 +1066,42 @@ msgstr ""
"<br>You can submit your question now and validate email after that. Your "
"question will saved as pending meanwhile. "
-#: templates/ask.html:112
+#: forum/skins/default/templates/ask.html:112
msgid "(required)"
msgstr ""
-#: templates/ask.html:119
+#: forum/skins/default/templates/ask.html:119
msgid "Login/signup to post your question"
msgstr "Login/Signup to Post"
-#: templates/ask.html:121
+#: forum/skins/default/templates/ask.html:121
msgid "Ask your question"
msgstr "Ask Your Question"
-#: templates/badge.html:6 templates/badge.html.py:17
+#: forum/skins/default/templates/badge.html:6
+#: forum/skins/default/templates/badge.html:17
msgid "Badge"
msgstr ""
-#: templates/badge.html:26
+#: forum/skins/default/templates/badge.html:26
msgid "The users have been awarded with badges:"
msgstr ""
-#: templates/badges.html:6
+#: forum/skins/default/templates/badges.html:6
msgid "Badges summary"
msgstr ""
-#: templates/badges.html:17
+#: forum/skins/default/templates/badges.html:17
msgid "Badges"
msgstr ""
-#: templates/badges.html:21
+#: forum/skins/default/templates/badges.html:21
msgid "Community gives you awards for your questions, answers and votes."
msgstr ""
"If your questions and answers are highly voted, your contribution to this "
"Q&amp;A community will be recognized with the variety of badges."
-#: templates/badges.html:22
+#: forum/skins/default/templates/badges.html:22
#, python-format
msgid ""
"Below is the list of available badges and number \n"
@@ -1342,161 +1116,176 @@ msgstr ""
"s'>feedback</a></strong> - what kinds of badges would you like to see and "
"suggest the activity for which those badges might be awarded."
-#: templates/badges.html:50
+#: forum/skins/default/templates/badges.html:50
msgid "Community badges"
msgstr "Badge levels"
-#: templates/badges.html:56
+#: forum/skins/default/templates/badges.html:56
msgid "gold badge description"
msgstr ""
"Gold badge is the highest award in this community. To obtain it have to show "
"profound knowledge and ability in addition to your active participation."
-#: templates/badges.html:64
+#: forum/skins/default/templates/badges.html:64
msgid "silver badge description"
msgstr ""
"Obtaining silver badge requires significant patience. If you have received "
"one, that means you have greatly contributed to this community."
-#: templates/badges.html:67
+#: forum/skins/default/templates/badges.html:67
msgid "bronze badge: often given as a special honor"
msgstr ""
-#: templates/badges.html:71
+#: forum/skins/default/templates/badges.html:71
msgid "bronze badge description"
msgstr ""
"If you are an active participant in this community, you will be recognized "
"with this badge."
-#: templates/book.html:7
+#: forum/skins/default/templates/book.html:7
msgid "reading channel"
msgstr ""
-#: templates/book.html:26
+#: forum/skins/default/templates/book.html:26
msgid "[author]"
msgstr ""
-#: templates/book.html:30
+#: forum/skins/default/templates/book.html:30
msgid "[publisher]"
msgstr ""
-#: templates/book.html:34
+#: forum/skins/default/templates/book.html:34
msgid "[publication date]"
msgstr ""
-#: templates/book.html:38
+#: forum/skins/default/templates/book.html:38
msgid "[price]"
msgstr ""
-#: templates/book.html:39
+#: forum/skins/default/templates/book.html:39
msgid "currency unit"
msgstr ""
-#: templates/book.html:42
+#: forum/skins/default/templates/book.html:42
msgid "[pages]"
msgstr ""
-#: templates/book.html:43
+#: forum/skins/default/templates/book.html:43
msgid "pages abbreviation"
msgstr ""
-#: templates/book.html:46
+#: forum/skins/default/templates/book.html:46
msgid "[tags]"
msgstr ""
-#: templates/book.html:56
+#: forum/skins/default/templates/book.html:56
msgid "author blog"
msgstr ""
-#: templates/book.html:62
+#: forum/skins/default/templates/book.html:62
msgid "book directory"
msgstr ""
-#: templates/book.html:66
+#: forum/skins/default/templates/book.html:66
msgid "buy online"
msgstr ""
-#: templates/book.html:79
+#: forum/skins/default/templates/book.html:79
msgid "reader questions"
msgstr ""
-#: templates/book.html:82
+#: forum/skins/default/templates/book.html:82
msgid "ask the author"
msgstr ""
-#: templates/book.html:88 templates/book.html.py:93
-#: templates/users_questions.html:18
+#: forum/skins/default/templates/book.html:88
+#: forum/skins/default/templates/book.html:93
+#: forum/skins/default/templates/users_questions.html:18
msgid "this question was selected as favorite"
msgstr ""
-#: templates/book.html:88 templates/book.html.py:93
-#: templates/users_questions.html:11 templates/users_questions.html.py:18
+#: forum/skins/default/templates/book.html:88
+#: forum/skins/default/templates/book.html:93
+#: forum/skins/default/templates/users_questions.html:11
+#: forum/skins/default/templates/users_questions.html:18
msgid "number of times"
msgstr ""
-#: templates/book.html:105 templates/index.html:49
-#: templates/question_summary_list_roll.html:14 templates/questions.html:80
-#: templates/unanswered.html:39 templates/users_questions.html:32
+#: forum/skins/default/templates/book.html:105
+#: forum/skins/default/templates/index.html:40
+#: forum/skins/default/templates/index_.html:40
+#: forum/skins/default/templates/question_summary_list_roll.html:14
+#: forum/skins/default/templates/questions.html:80
+#: forum/skins/default/templates/users_questions.html:32
msgid "votes"
msgstr ""
-#: templates/book.html:108
+#: forum/skins/default/templates/book.html:108
msgid "the answer has been accepted to be correct"
msgstr ""
-#: templates/book.html:115 templates/index.html:50
-#: templates/question_summary_list_roll.html:15 templates/questions.html:81
-#: templates/unanswered.html:40 templates/users_questions.html:40
+#: forum/skins/default/templates/book.html:115
+#: forum/skins/default/templates/index.html:48
+#: forum/skins/default/templates/index_.html:48
+#: forum/skins/default/templates/question_summary_list_roll.html:15
+#: forum/skins/default/templates/questions.html:81
+#: forum/skins/default/templates/users_questions.html:40
msgid "views"
msgstr ""
-#: templates/book.html:125 templates/index.html:105
-#: templates/question.html:480 templates/question_summary_list_roll.html:52
-#: templates/questions.html:136 templates/tags.html:49
-#: templates/unanswered.html:95 templates/unanswered.html.py:122
-#: templates/users_questions.html:52
+#: forum/skins/default/templates/book.html:125
+#: forum/skins/default/templates/index.html:63
+#: forum/skins/default/templates/index_.html:63
+#: forum/skins/default/templates/question.html:478
+#: forum/skins/default/templates/question_summary_list_roll.html:52
+#: forum/skins/default/templates/questions.html:136
+#: forum/skins/default/templates/tags.html:49
+#: forum/skins/default/templates/users_questions.html:52
msgid "using tags"
msgstr ""
-#: templates/book.html:147
+#: forum/skins/default/templates/book.html:147
msgid "subscribe to book RSS feed"
msgstr ""
-#: templates/book.html:147 templates/index.html:156
+#: forum/skins/default/templates/book.html:147
+#: forum/skins/default/templates/index.html:114
+#: forum/skins/default/templates/index_.html:114
msgid "subscribe to the questions feed"
msgstr ""
-#: templates/close.html:6 templates/close.html.py:16
+#: forum/skins/default/templates/close.html:6
+#: forum/skins/default/templates/close.html:16
msgid "Close question"
msgstr ""
-#: templates/close.html:19
+#: forum/skins/default/templates/close.html:19
msgid "Close the question"
msgstr ""
-#: templates/close.html:25
+#: forum/skins/default/templates/close.html:25
msgid "Reasons"
msgstr ""
-#: templates/close.html:28
+#: forum/skins/default/templates/close.html:28
msgid "OK to close"
msgstr ""
-#: templates/faq.html:11
+#: forum/skins/default/templates/faq.html:11
msgid "Frequently Asked Questions "
msgstr ""
-#: templates/faq.html:16
+#: forum/skins/default/templates/faq.html:16
msgid "What kinds of questions can I ask here?"
msgstr ""
-#: templates/faq.html:17
+#: forum/skins/default/templates/faq.html:17
msgid ""
"Most importanly - questions should be <strong>relevant</strong> to this "
"community."
msgstr ""
-#: templates/faq.html:18
+#: forum/skins/default/templates/faq.html:18
msgid ""
"Before asking the question - please make sure to use search to see whether "
"your question has alredy been answered."
@@ -1504,21 +1293,21 @@ msgstr ""
"Before you ask - please make sure to search for a similar question. You can "
"search questions by their title or tags."
-#: templates/faq.html:21
+#: forum/skins/default/templates/faq.html:21
msgid "What questions should I avoid asking?"
msgstr "What kinds of questions should be avoided?"
-#: templates/faq.html:22
+#: forum/skins/default/templates/faq.html:22
msgid ""
"Please avoid asking questions that are not relevant to this community, too "
"subjective and argumentative."
msgstr ""
-#: templates/faq.html:27
+#: forum/skins/default/templates/faq.html:27
msgid "What should I avoid in my answers?"
msgstr ""
-#: templates/faq.html:28
+#: forum/skins/default/templates/faq.html:28
msgid ""
"is a Q&A site, not a discussion group. Therefore - please avoid having "
"discussions in your answers, comment facility allows some space for brief "
@@ -1529,19 +1318,19 @@ msgstr ""
"they tend to dilute the essense of questions and answers. For the brief "
"discussions please use commenting facility."
-#: templates/faq.html:32
+#: forum/skins/default/templates/faq.html:32
msgid "Who moderates this community?"
msgstr ""
-#: templates/faq.html:33
+#: forum/skins/default/templates/faq.html:33
msgid "The short answer is: <strong>you</strong>."
msgstr ""
-#: templates/faq.html:34
+#: forum/skins/default/templates/faq.html:34
msgid "This website is moderated by the users."
msgstr ""
-#: templates/faq.html:35
+#: forum/skins/default/templates/faq.html:35
msgid ""
"The reputation system allows users earn the authorization to perform a "
"variety of moderation tasks."
@@ -1549,11 +1338,11 @@ msgstr ""
"Karma system allows users to earn rights to perform a variety of moderation "
"tasks"
-#: templates/faq.html:40
+#: forum/skins/default/templates/faq.html:40
msgid "How does reputation system work?"
msgstr "How does karma system work?"
-#: templates/faq.html:41
+#: forum/skins/default/templates/faq.html:41
msgid "Rep system summary"
msgstr ""
"When a question or answer is upvoted, the user who posted them will gain "
@@ -1561,7 +1350,7 @@ msgstr ""
"rough measure of the community trust to him/her. Various moderation tasks "
"are gradually assigned to the users based on those points."
-#: templates/faq.html:42
+#: forum/skins/default/templates/faq.html:42
msgid ""
"For example, if you ask an interesting question or give a helpful answer, "
"your input will be upvoted. On the other hand if the answer is misleading - "
@@ -1572,55 +1361,57 @@ msgid ""
"type of moderation task."
msgstr ""
-#: templates/faq.html:53 templates/user_votes.html:15
+#: forum/skins/default/templates/faq.html:53
+#: forum/skins/default/templates/user_votes.html:15
msgid "upvote"
msgstr ""
-#: templates/faq.html:57
+#: forum/skins/default/templates/faq.html:57
msgid "use tags"
msgstr ""
-#: templates/faq.html:62
+#: forum/skins/default/templates/faq.html:62
msgid "add comments"
msgstr ""
-#: templates/faq.html:66 templates/user_votes.html:17
+#: forum/skins/default/templates/faq.html:66
+#: forum/skins/default/templates/user_votes.html:17
msgid "downvote"
msgstr ""
-#: templates/faq.html:69
+#: forum/skins/default/templates/faq.html:69
msgid "open and close own questions"
msgstr ""
-#: templates/faq.html:73
+#: forum/skins/default/templates/faq.html:73
msgid "retag questions"
msgstr ""
-#: templates/faq.html:78
+#: forum/skins/default/templates/faq.html:78
msgid "edit community wiki questions"
msgstr ""
-#: templates/faq.html:83
+#: forum/skins/default/templates/faq.html:83
msgid "edit any answer"
msgstr ""
-#: templates/faq.html:87
+#: forum/skins/default/templates/faq.html:87
msgid "open any closed question"
msgstr ""
-#: templates/faq.html:91
+#: forum/skins/default/templates/faq.html:91
msgid "delete any comment"
msgstr ""
-#: templates/faq.html:95
+#: forum/skins/default/templates/faq.html:95
msgid "delete any questions and answers and perform other moderation tasks"
msgstr ""
-#: templates/faq.html:102
+#: forum/skins/default/templates/faq.html:102
msgid "how to validate email title"
msgstr "How to validate email and why?"
-#: templates/faq.html:104
+#: forum/skins/default/templates/faq.html:104
#, python-format
msgid ""
"how to validate email info with %(send_email_key_url)s %(gravatar_faq_url)s"
@@ -1638,11 +1429,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>"
-#: templates/faq.html:108
+#: forum/skins/default/templates/faq.html:108
msgid "what is gravatar"
msgstr "What is gravatar?"
-#: templates/faq.html:109
+#: forum/skins/default/templates/faq.html:109
msgid "gravatar faq info"
msgstr ""
"<strong>Gravatar</strong> means <strong>g</strong>lobally <strong>r</"
@@ -1653,44 +1444,44 @@ msgstr ""
"your image</strong> at <a href='http://gravatar.com'><strong>gravatar.com</"
"strong></a>"
-#: templates/faq.html:112
+#: forum/skins/default/templates/faq.html:112
msgid "To register, do I need to create new password?"
msgstr ""
-#: templates/faq.html:113
+#: forum/skins/default/templates/faq.html:113
msgid ""
"No, you don't have to. You can login through any service that supports "
"OpenID, e.g. Google, Yahoo, AOL, etc."
msgstr ""
-#: templates/faq.html:114
+#: forum/skins/default/templates/faq.html:114
msgid "Login now!"
msgstr ""
-#: templates/faq.html:119
+#: forum/skins/default/templates/faq.html:119
msgid "Why other people can edit my questions/answers?"
msgstr ""
-#: templates/faq.html:120
+#: forum/skins/default/templates/faq.html:120
msgid "Goal of this site is..."
msgstr ""
-#: templates/faq.html:120
+#: forum/skins/default/templates/faq.html:120
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 ""
-#: templates/faq.html:121
+#: forum/skins/default/templates/faq.html:121
msgid "If this approach is not for you, we respect your choice."
msgstr ""
-#: templates/faq.html:125
+#: forum/skins/default/templates/faq.html:125
msgid "Still have questions?"
msgstr ""
-#: templates/faq.html:126
+#: forum/skins/default/templates/faq.html:126
#, python-format
msgid ""
"Please ask your question at %(ask_question_url)s, help make our community "
@@ -1699,23 +1490,27 @@ msgstr ""
"Please <a href='%(ask_question_url)s'>ask</a> your question, help make our "
"community better!"
-#: templates/faq.html:128 templates/header.html:27 templates/header.html.py:61
+#: forum/skins/default/templates/faq.html:128
+#: forum/skins/default/templates/header.html:27
+#: forum/skins/default/templates/header.html:55
msgid "questions"
msgstr ""
-#: templates/faq.html:128 templates/index.html:161
+#: forum/skins/default/templates/faq.html:128
+#: forum/skins/default/templates/index.html:121
+#: forum/skins/default/templates/index_.html:121
msgid "."
msgstr ""
-#: templates/feedback.html:6
+#: forum/skins/default/templates/feedback.html:6
msgid "Feedback"
msgstr ""
-#: templates/feedback.html:11
+#: forum/skins/default/templates/feedback.html:11
msgid "Give us your feedback!"
msgstr ""
-#: templates/feedback.html:17
+#: forum/skins/default/templates/feedback.html:17
#, python-format
msgid ""
"\n"
@@ -1725,7 +1520,7 @@ msgid ""
" "
msgstr ""
-#: templates/feedback.html:24
+#: forum/skins/default/templates/feedback.html:24
msgid ""
"\n"
" <span class='big strong'>Dear visitor</span>, we look forward to "
@@ -1734,218 +1529,249 @@ msgid ""
" "
msgstr ""
-#: templates/feedback.html:41
+#: forum/skins/default/templates/feedback.html:41
msgid "(this field is required)"
msgstr ""
-#: templates/feedback.html:49
+#: forum/skins/default/templates/feedback.html:49
msgid "Send Feedback"
msgstr ""
-#: templates/feedback_email.txt:3
+#: forum/skins/default/templates/feedback_email.txt:3
#, python-format
msgid ""
"\n"
"Hello, this is a %(site_title)s forum feedback message\n"
msgstr ""
-#: templates/feedback_email.txt:9
+#: forum/skins/default/templates/feedback_email.txt:9
msgid "Sender is"
msgstr ""
-#: templates/feedback_email.txt:11 templates/feedback_email.txt.py:14
+#: forum/skins/default/templates/feedback_email.txt:11
+#: forum/skins/default/templates/feedback_email.txt:14
msgid "email"
msgstr ""
-#: templates/feedback_email.txt:13
+#: forum/skins/default/templates/feedback_email.txt:13
msgid "anonymous"
msgstr ""
-#: templates/feedback_email.txt:19
+#: forum/skins/default/templates/feedback_email.txt:19
msgid "Message body:"
msgstr ""
-#: templates/footer.html:8 templates/header.html:13 templates/index.html:119
+#: forum/skins/default/templates/footer.html:8
+#: forum/skins/default/templates/header.html:13
+#: forum/skins/default/templates/index.html:77
+#: forum/skins/default/templates/index_.html:77
msgid "about"
msgstr ""
-#: templates/footer.html:9 templates/header.html:14 templates/index.html:120
-#: templates/question_edit_tips.html:17
+#: forum/skins/default/templates/footer.html:9
+#: forum/skins/default/templates/header.html:14
+#: forum/skins/default/templates/index.html:78
+#: forum/skins/default/templates/index_.html:78
+#: forum/skins/default/templates/question_edit_tips.html:17
msgid "faq"
msgstr ""
-#: templates/footer.html:10
-msgid "blog"
-msgstr ""
-
-#: templates/footer.html:11
-msgid "contact us"
-msgstr ""
-
-#: templates/footer.html:12
+#: forum/skins/default/templates/footer.html:10
msgid "privacy policy"
msgstr ""
-#: templates/footer.html:21
+#: forum/skins/default/templates/footer.html:19
msgid "give feedback"
msgstr ""
-#: templates/header.html:9
+#: forum/skins/default/templates/header.html:9
msgid "logout"
msgstr ""
-#: templates/header.html:11
+#: forum/skins/default/templates/header.html:11
msgid "login"
msgstr ""
-#: templates/header.html:21
+#: forum/skins/default/templates/header.html:21
msgid "back to home page"
msgstr ""
-#: templates/header.html:29 templates/header.html.py:63
+#: forum/skins/default/templates/header.html:29
+#: forum/skins/default/templates/header.html:57
msgid "users"
msgstr ""
-#: templates/header.html:31
+#: forum/skins/default/templates/header.html:31
msgid "books"
msgstr ""
-#: templates/header.html:34
+#: forum/skins/default/templates/header.html:33
+#: forum/templatetags/extra_tags.py:165 forum/templatetags/extra_tags.py:194
+msgid "badges"
+msgstr ""
+
+#: forum/skins/default/templates/header.html:34
msgid "unanswered questions"
msgstr "unanswered"
-#: templates/header.html:38
-msgid "my profile"
-msgstr ""
-
-#: templates/header.html:42
+#: forum/skins/default/templates/header.html:36
msgid "ask a question"
msgstr ""
-#: templates/header.html:57
+#: forum/skins/default/templates/header.html:51
msgid "search"
msgstr ""
-#: templates/index.html:8
+#: forum/skins/default/templates/index.html:8
+#: forum/skins/default/templates/index_.html:8
msgid "Home"
msgstr ""
-#: templates/index.html:25 templates/questions.html:8
+#: forum/skins/default/templates/index.html:25
+#: forum/skins/default/templates/index_.html:25
+#: forum/skins/default/templates/questions.html:8
msgid "Questions"
msgstr ""
-#: templates/index.html:27
+#: forum/skins/default/templates/index.html:27
+#: forum/skins/default/templates/index_.html:27
msgid "last updated questions"
msgstr ""
-#: templates/index.html:27 templates/questions.html:47
-#: templates/unanswered.html:21
+#: forum/skins/default/templates/index.html:27
+#: forum/skins/default/templates/index_.html:27
+#: forum/skins/default/templates/questions.html:47
msgid "newest"
msgstr ""
-#: templates/index.html:28 templates/questions.html:49
+#: forum/skins/default/templates/index.html:28
+#: forum/skins/default/templates/index_.html:28
+#: forum/skins/default/templates/questions.html:49
msgid "hottest questions"
msgstr ""
-#: templates/index.html:28 templates/questions.html:49
+#: forum/skins/default/templates/index.html:28
+#: forum/skins/default/templates/index_.html:28
+#: forum/skins/default/templates/questions.html:49
msgid "hottest"
msgstr ""
-#: templates/index.html:29 templates/questions.html:50
+#: forum/skins/default/templates/index.html:29
+#: forum/skins/default/templates/index_.html:29
+#: forum/skins/default/templates/questions.html:50
msgid "most voted questions"
msgstr ""
-#: templates/index.html:29 templates/questions.html:50
+#: forum/skins/default/templates/index.html:29
+#: forum/skins/default/templates/index_.html:29
+#: forum/skins/default/templates/questions.html:50
msgid "most voted"
msgstr ""
-#: templates/index.html:30
+#: forum/skins/default/templates/index.html:30
+#: forum/skins/default/templates/index_.html:30
msgid "all questions"
msgstr ""
-#: templates/index.html:48 templates/question_summary_list_roll.html:13
-#: templates/questions.html:79 templates/unanswered.html:38
-#: templates/users_questions.html:36
-msgid "answers"
-msgstr ""
-
-#: templates/index.html:80 templates/index.html.py:94
-#: templates/questions.html:111 templates/questions.html.py:125
-#: templates/unanswered.html:70 templates/unanswered.html.py:84
-msgid "Posted:"
+#: forum/skins/default/templates/index.html:42
+#: forum/skins/default/templates/index_.html:42
+#: forum/skins/default/templates/users_questions.html:34
+msgid "this answer has been accepted to be correct"
msgstr ""
-#: templates/index.html:83 templates/index.html.py:88
-#: templates/questions.html:114 templates/questions.html.py:119
-#: templates/unanswered.html:73 templates/unanswered.html.py:78
-msgid "Updated:"
+#: forum/skins/default/templates/index.html:44
+#: forum/skins/default/templates/index_.html:44
+#: forum/skins/default/templates/question_summary_list_roll.html:13
+#: forum/skins/default/templates/questions.html:79
+#: forum/skins/default/templates/users_questions.html:36
+msgid "answers"
msgstr ""
-#: templates/index.html:105 templates/question.html:480
-#: templates/question_summary_list_roll.html:52 templates/questions.html:136
-#: templates/tags.html:49 templates/unanswered.html:95
-#: templates/unanswered.html.py:122 templates/users_questions.html:52
+#: forum/skins/default/templates/index.html:63
+#: forum/skins/default/templates/index_.html:63
+#: forum/skins/default/templates/question.html:478
+#: forum/skins/default/templates/question_summary_list_roll.html:52
+#: forum/skins/default/templates/questions.html:136
+#: forum/skins/default/templates/tags.html:49
+#: forum/skins/default/templates/users_questions.html:52
msgid "see questions tagged"
msgstr ""
-#: templates/index.html:116
+#: forum/skins/default/templates/index.html:74
+#: forum/skins/default/templates/index_.html:74
msgid "welcome to website"
msgstr "Welcome to Q&amp;A forum"
-#: templates/index.html:127
+#: forum/skins/default/templates/index.html:85
+#: forum/skins/default/templates/index_.html:85
msgid "Recent tags"
msgstr ""
-#: templates/index.html:132 templates/question.html:135
+#: forum/skins/default/templates/index.html:90
+#: forum/skins/default/templates/index_.html:90
+#: forum/skins/default/templates/question.html:133
#, python-format
msgid "see questions tagged '%(tagname)s'"
msgstr ""
-#: templates/index.html:135 templates/index.html.py:161
+#: forum/skins/default/templates/index.html:93
+#: forum/skins/default/templates/index.html:121
+#: forum/skins/default/templates/index_.html:93
+#: forum/skins/default/templates/index_.html:121
msgid "popular tags"
msgstr "tags"
-#: templates/index.html:140
+#: forum/skins/default/templates/index.html:98
+#: forum/skins/default/templates/index_.html:98
msgid "Recent awards"
msgstr "Recent badges"
-#: templates/index.html:146
+#: forum/skins/default/templates/index.html:104
msgid "given to"
msgstr ""
-#: templates/index.html:151
+#: forum/skins/default/templates/index.html:109
+#: forum/skins/default/templates/index_.html:109
msgid "all awards"
msgstr "all badges"
-#: templates/index.html:156
+#: forum/skins/default/templates/index.html:114
+#: forum/skins/default/templates/index_.html:114
msgid "subscribe to last 30 questions by RSS"
msgstr ""
-#: templates/index.html:161
+#: forum/skins/default/templates/index.html:121
+#: forum/skins/default/templates/index_.html:121
msgid "Still looking for more? See"
msgstr ""
-#: templates/index.html:161
+#: forum/skins/default/templates/index.html:121
+#: forum/skins/default/templates/index_.html:121
msgid "complete list of questions"
msgstr "list of all questions"
-#: templates/index.html:161 templates/authopenid/signup.html:18
+#: forum/skins/default/templates/index.html:121
+#: forum/skins/default/templates/index_.html:121
+#: forum/skins/default/templates/authopenid/signup.html:28
msgid "or"
msgstr ""
-#: templates/index.html:161
+#: forum/skins/default/templates/index.html:121
+#: forum/skins/default/templates/index_.html:121
msgid "Please help us answer"
msgstr ""
-#: templates/index.html:161
+#: forum/skins/default/templates/index.html:121
+#: forum/skins/default/templates/index_.html:121
msgid "list of unanswered questions"
msgstr "unanswered questions"
-#: templates/logout.html:6 templates/logout.html.py:16
+#: forum/skins/default/templates/logout.html:6
+#: forum/skins/default/templates/logout.html:16
msgid "Logout"
msgstr ""
-#: templates/logout.html:19
+#: forum/skins/default/templates/logout.html:19
msgid ""
"As a registered user you can login with your OpenID, log out of the site or "
"permanently remove your account."
@@ -1954,35 +1780,46 @@ msgstr ""
"sign you off from your OpenID provider.</p><p>If you wish to sign off "
"completely - please make sure to log out from your OpenID provider as well."
-#: templates/logout.html:20
+#: forum/skins/default/templates/logout.html:20
msgid "Logout now"
msgstr "Logout Now"
-#: templates/pagesize.html:6
+#: forum/skins/default/templates/notarobot.html:3
+msgid "Please prove that you are a Human Being"
+msgstr ""
+
+#: forum/skins/default/templates/notarobot.html:10
+msgid "I am a Human Being"
+msgstr ""
+
+#: forum/skins/default/templates/pagesize.html:6
msgid "posts per page"
msgstr ""
-#: templates/paginator.html:6 templates/paginator.html.py:7
+#: forum/skins/default/templates/paginator.html:6
+#: forum/skins/default/templates/paginator.html:7
msgid "previous"
msgstr ""
-#: templates/paginator.html:19
+#: forum/skins/default/templates/paginator.html:19
msgid "current page"
msgstr ""
-#: templates/paginator.html:22 templates/paginator.html.py:29
+#: forum/skins/default/templates/paginator.html:22
+#: forum/skins/default/templates/paginator.html:29
msgid "page number "
msgstr ""
-#: templates/paginator.html:22 templates/paginator.html.py:29
+#: forum/skins/default/templates/paginator.html:22
+#: forum/skins/default/templates/paginator.html:29
msgid "number - make blank in english"
msgstr ""
-#: templates/paginator.html:33
+#: forum/skins/default/templates/paginator.html:33
msgid "next page"
msgstr ""
-#: templates/post_contributor_info.html:9
+#: forum/skins/default/templates/post_contributor_info.html:9
#, python-format
msgid ""
"\n"
@@ -1995,141 +1832,156 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/post_contributor_info.html:19
+#: forum/skins/default/templates/post_contributor_info.html:19
msgid "asked"
msgstr ""
-#: templates/post_contributor_info.html:22
+#: forum/skins/default/templates/post_contributor_info.html:22
msgid "answered"
msgstr ""
-#: templates/post_contributor_info.html:24
+#: forum/skins/default/templates/post_contributor_info.html:24
msgid "posted"
msgstr ""
-#: templates/post_contributor_info.html:45
+#: forum/skins/default/templates/post_contributor_info.html:45
msgid "updated"
msgstr ""
-#: templates/privacy.html:6 templates/privacy.html.py:11
+#: forum/skins/default/templates/privacy.html:6
+#: forum/skins/default/templates/privacy.html:11
msgid "Privacy policy"
msgstr ""
-#: templates/privacy.html:15
+#: forum/skins/default/templates/privacy.html:15
msgid "general message about privacy"
msgstr ""
"Respecting users privacy is an important core principle of this Q&amp;A "
"forum. Information on this page details how this forum protects your "
"privacy, and what type of information is collected."
-#: templates/privacy.html:18
+#: forum/skins/default/templates/privacy.html:18
msgid "Site Visitors"
msgstr ""
-#: templates/privacy.html:20
+#: forum/skins/default/templates/privacy.html:20
msgid "what technical information is collected about visitors"
msgstr ""
"Information on question views, revisions of questions and answers - both "
"times and content are recorded for each user in order to correctly count "
"number of views, maintain data integrity and report relevant updates."
-#: templates/privacy.html:23
+#: forum/skins/default/templates/privacy.html:23
msgid "Personal Information"
msgstr ""
-#: templates/privacy.html:25
+#: forum/skins/default/templates/privacy.html:25
msgid "details on personal information policies"
msgstr ""
"Members of this community may choose to display personally identifiable "
"information in their profiles. Forum will never display such information "
"without a request from the user."
-#: templates/privacy.html:28
+#: forum/skins/default/templates/privacy.html:28
msgid "Other Services"
msgstr ""
-#: templates/privacy.html:30
+#: forum/skins/default/templates/privacy.html:30
msgid "details on sharing data with third parties"
msgstr ""
"None of the data that is not openly shown on the forum by the choice of the "
"user is shared with any third party."
-#: templates/privacy.html:35
+#: forum/skins/default/templates/privacy.html:35
msgid "cookie policy details"
msgstr ""
"Forum software relies on the internet cookie technology to keep track of "
"user sessions. Cookies must be enabled in your browser so that forum can "
"work for you."
-#: templates/privacy.html:37
+#: forum/skins/default/templates/privacy.html:37
msgid "Policy Changes"
msgstr ""
-#: templates/privacy.html:38
+#: forum/skins/default/templates/privacy.html:38
msgid "how privacy policies can be changed"
msgstr ""
"These policies may be adjusted to improve protection of user's privacy. "
"Whenever such changes occur, users will be notified via the internal "
"messaging system. "
-#: templates/question.html:77 templates/question.html.py:78
-#: templates/question.html:94 templates/question.html.py:96
+#: forum/skins/default/templates/question.html:77
+#: forum/skins/default/templates/question.html:78
+#: forum/skins/default/templates/question.html:94
+#: forum/skins/default/templates/question.html:96
msgid "i like this post (click again to cancel)"
msgstr ""
-#: templates/question.html:80 templates/question.html.py:98
-#: templates/question.html:257
+#: forum/skins/default/templates/question.html:80
+#: forum/skins/default/templates/question.html:98
+#: forum/skins/default/templates/question.html:255
msgid "current number of votes"
msgstr ""
-#: templates/question.html:89 templates/question.html.py:90
-#: templates/question.html:103 templates/question.html.py:104
+#: forum/skins/default/templates/question.html:89
+#: forum/skins/default/templates/question.html:90
+#: forum/skins/default/templates/question.html:103
+#: forum/skins/default/templates/question.html:104
msgid "i dont like this post (click again to cancel)"
msgstr ""
-#: templates/question.html:109 templates/question.html.py:110
+#: forum/skins/default/templates/question.html:108
+#: forum/skins/default/templates/question.html:109
msgid "mark this question as favorite (click again to cancel)"
msgstr ""
-#: templates/question.html:116 templates/question.html.py:117
+#: forum/skins/default/templates/question.html:115
+#: forum/skins/default/templates/question.html:116
msgid "remove favorite mark from this question (click again to restore mark)"
msgstr ""
-#: templates/question.html:140 templates/question.html.py:294
-#: templates/revisions_answer.html:58 templates/revisions_question.html:58
+#: forum/skins/default/templates/question.html:138
+#: forum/skins/default/templates/question.html:292
+#: forum/skins/default/templates/revisions_answer.html:58
+#: forum/skins/default/templates/revisions_question.html:58
msgid "edit"
msgstr ""
-#: templates/question.html:145
+#: forum/skins/default/templates/question.html:143
msgid "reopen"
msgstr ""
-#: templates/question.html:149
+#: forum/skins/default/templates/question.html:147
msgid "close"
msgstr ""
-#: templates/question.html:155 templates/question.html.py:299
+#: forum/skins/default/templates/question.html:153
+#: forum/skins/default/templates/question.html:297
msgid ""
"report as offensive (i.e containing spam, advertising, malicious text, etc.)"
msgstr ""
-#: templates/question.html:156 templates/question.html.py:300
+#: forum/skins/default/templates/question.html:154
+#: forum/skins/default/templates/question.html:298
msgid "flag offensive"
msgstr ""
-#: templates/question.html:164 templates/question.html.py:311
+#: forum/skins/default/templates/question.html:162
+#: forum/skins/default/templates/question.html:309
msgid "delete"
msgstr ""
-#: templates/question.html:182 templates/question.html.py:331
+#: forum/skins/default/templates/question.html:180
+#: forum/skins/default/templates/question.html:329
msgid "delete this comment"
msgstr ""
-#: templates/question.html:193 templates/question.html.py:342
+#: forum/skins/default/templates/question.html:191
+#: forum/skins/default/templates/question.html:340
msgid "add comment"
msgstr "post a comment"
-#: templates/question.html:197
+#: forum/skins/default/templates/question.html:195
#, python-format
msgid ""
"\n"
@@ -2143,7 +1995,7 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/question.html:203
+#: forum/skins/default/templates/question.html:201
#, python-format
msgid ""
"\n"
@@ -2158,18 +2010,18 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/question.html:219
+#: forum/skins/default/templates/question.html:217
#, python-format
msgid ""
"The question has been closed for the following reason \"%(close_reason)s\" by"
msgstr ""
-#: templates/question.html:221
+#: forum/skins/default/templates/question.html:219
#, python-format
msgid "close date %(closed_at)s"
msgstr ""
-#: templates/question.html:229
+#: forum/skins/default/templates/question.html:227
#, python-format
msgid ""
"\n"
@@ -2182,59 +2034,63 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/question.html:237
+#: forum/skins/default/templates/question.html:235
msgid "oldest answers will be shown first"
msgstr ""
-#: templates/question.html:237
+#: forum/skins/default/templates/question.html:235
msgid "oldest answers"
msgstr "oldest"
-#: templates/question.html:239
+#: forum/skins/default/templates/question.html:237
msgid "newest answers will be shown first"
msgstr ""
-#: templates/question.html:239
+#: forum/skins/default/templates/question.html:237
msgid "newest answers"
msgstr "newest"
-#: templates/question.html:241
+#: forum/skins/default/templates/question.html:239
msgid "most voted answers will be shown first"
msgstr ""
-#: templates/question.html:241
+#: forum/skins/default/templates/question.html:239
msgid "popular answers"
msgstr "most voted"
-#: templates/question.html:255 templates/question.html.py:256
+#: forum/skins/default/templates/question.html:253
+#: forum/skins/default/templates/question.html:254
msgid "i like this answer (click again to cancel)"
msgstr ""
-#: templates/question.html:262 templates/question.html.py:263
+#: forum/skins/default/templates/question.html:260
+#: forum/skins/default/templates/question.html:261
msgid "i dont like this answer (click again to cancel)"
msgstr ""
-#: templates/question.html:268 templates/question.html.py:269
+#: forum/skins/default/templates/question.html:266
+#: forum/skins/default/templates/question.html:267
msgid "mark this answer as favorite (click again to undo)"
msgstr ""
-#: templates/question.html:274 templates/question.html.py:275
+#: forum/skins/default/templates/question.html:272
+#: forum/skins/default/templates/question.html:273
msgid "the author of the question has selected this answer as correct"
msgstr ""
-#: templates/question.html:288
+#: forum/skins/default/templates/question.html:286
msgid "answer permanent link"
msgstr ""
-#: templates/question.html:289
+#: forum/skins/default/templates/question.html:287
msgid "permanent link"
msgstr "link"
-#: templates/question.html:311
+#: forum/skins/default/templates/question.html:309
msgid "undelete"
msgstr ""
-#: templates/question.html:346
+#: forum/skins/default/templates/question.html:344
#, python-format
msgid ""
"\n"
@@ -2249,7 +2105,7 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/question.html:352
+#: forum/skins/default/templates/question.html:350
#, python-format
msgid ""
"\n"
@@ -2264,18 +2120,19 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/question.html:378 templates/question.html.py:381
+#: forum/skins/default/templates/question.html:376
+#: forum/skins/default/templates/question.html:379
msgid "Notify me once a day when there are any new answers"
msgstr ""
"<strong>Notify me</strong> once a day by email when there are any new "
"answers or updates"
-#: templates/question.html:384
+#: forum/skins/default/templates/question.html:382
msgid "Notify me weekly when there are any new answers"
msgstr ""
"<strong>Notify me</strong> weekly when there are any new answers or updates"
-#: templates/question.html:389
+#: forum/skins/default/templates/question.html:387
#, python-format
msgid ""
"\n"
@@ -2287,21 +2144,21 @@ msgstr ""
"(note: you can always <a href='%(profile_url)s?"
"sort=email_subscriptions'>adjust frequency</a> of email updates)"
-#: templates/question.html:396
+#: forum/skins/default/templates/question.html:394
msgid "once you sign in you will be able to subscribe for any updates here"
msgstr ""
"<span class='strong'>Here</span> (once you log in) you will be able to sign "
"up for the periodic email updates about this question."
-#: templates/question.html:407
+#: forum/skins/default/templates/question.html:405
msgid "Your answer"
msgstr ""
-#: templates/question.html:409
+#: forum/skins/default/templates/question.html:407
msgid "Be the first one to answer this question!"
msgstr ""
-#: templates/question.html:415
+#: forum/skins/default/templates/question.html:413
msgid "you can answer anonymously and then login"
msgstr ""
"<span class='strong big'>Please start posting your answer anonymously</span> "
@@ -2310,7 +2167,7 @@ msgstr ""
"answer</strong>, for discussions, <strong>please use comments</strong> and "
"<strong>please do remember to vote</strong> (after you log in)!"
-#: templates/question.html:419
+#: forum/skins/default/templates/question.html:417
msgid "answer your own question only to give an answer"
msgstr ""
"<span class='big strong'>You are welcome to answer your own question</span>, "
@@ -2320,7 +2177,7 @@ msgstr ""
"forget to vote :)</strong> for the answers that you liked (or perhaps did "
"not like)! "
-#: templates/question.html:421
+#: forum/skins/default/templates/question.html:419
msgid "please only give an answer, no discussions"
msgstr ""
"<span class='big strong'>Please try to give a substantial answer</span>. If "
@@ -2330,124 +2187,135 @@ msgstr ""
"please <strong>don't forget to vote</strong> - it really helps to select the "
"best questions and answers!"
-#: templates/question.html:457
+#: forum/skins/default/templates/question.html:455
msgid "Login/Signup to Post Your Answer"
msgstr ""
-#: templates/question.html:460
+#: forum/skins/default/templates/question.html:458
msgid "Answer Your Own Question"
msgstr ""
-#: templates/question.html:462
+#: forum/skins/default/templates/question.html:460
msgid "Answer the question"
msgstr "Post Your Answer"
-#: templates/question.html:475
+#: forum/skins/default/templates/question.html:473
msgid "Question tags"
msgstr "Tags"
-#: templates/question.html:485
+#: forum/skins/default/templates/question.html:483
msgid "question asked"
msgstr "Asked"
-#: templates/question.html:488
+#: forum/skins/default/templates/question.html:486
msgid "question was seen"
msgstr "Seen"
-#: templates/question.html:488
+#: forum/skins/default/templates/question.html:486
msgid "times"
msgstr ""
-#: templates/question.html:491
+#: forum/skins/default/templates/question.html:489
msgid "last updated"
msgstr "Last updated"
-#: templates/question.html:496
+#: forum/skins/default/templates/question.html:494
msgid "Related questions"
msgstr ""
-#: templates/question_edit.html:5 templates/question_edit.html.py:66
+#: forum/skins/default/templates/question_edit.html:5
+#: forum/skins/default/templates/question_edit.html:66
msgid "Edit question"
msgstr ""
-#: templates/question_edit_tips.html:4
+#: forum/skins/default/templates/question_edit_tips.html:4
msgid "question tips"
msgstr "Tips"
-#: templates/question_edit_tips.html:7
+#: forum/skins/default/templates/question_edit_tips.html:7
msgid "please ask a relevant question"
msgstr "ask a question relevant to the CNPROG community"
-#: templates/question_edit_tips.html:10
+#: forum/skins/default/templates/question_edit_tips.html:10
msgid "please try provide enough details"
msgstr "provide enough details"
-#: templates/question_retag.html:4 templates/question_retag.html.py:53
+#: forum/skins/default/templates/question_retag.html:4
+#: forum/skins/default/templates/question_retag.html:53
msgid "Change tags"
msgstr ""
-#: templates/question_retag.html:40
+#: forum/skins/default/templates/question_retag.html:40
msgid "up to 5 tags, less than 20 characters each"
msgstr ""
-#: templates/question_retag.html:83
+#: forum/skins/default/templates/question_retag.html:83
msgid "Why use and modify tags?"
msgstr ""
-#: templates/question_retag.html:86
+#: forum/skins/default/templates/question_retag.html:86
msgid "tags help us keep Questions organized"
msgstr ""
-#: templates/question_retag.html:94
+#: forum/skins/default/templates/question_retag.html:94
msgid "tag editors receive special awards from the community"
msgstr ""
-#: templates/questions.html:29
+#: forum/skins/default/templates/questions.html:29
msgid "Found by tags"
msgstr "Tagged questions"
-#: templates/questions.html:33
+#: forum/skins/default/templates/questions.html:33
msgid "Search results"
msgstr ""
-#: templates/questions.html:35
+#: forum/skins/default/templates/questions.html:35
msgid "Found by title"
msgstr ""
-#: templates/questions.html:39 templates/unanswered.html:8
-#: templates/unanswered.html.py:19
+#: forum/skins/default/templates/questions.html:39
msgid "Unanswered questions"
msgstr ""
-#: templates/questions.html:41
+#: forum/skins/default/templates/questions.html:41
msgid "All questions"
msgstr ""
-#: templates/questions.html:47 templates/unanswered.html:21
+#: forum/skins/default/templates/questions.html:47
msgid "most recently asked questions"
msgstr ""
-#: templates/questions.html:48
+#: forum/skins/default/templates/questions.html:48
msgid "most recently updated questions"
msgstr ""
-#: templates/questions.html:48
+#: forum/skins/default/templates/questions.html:48
msgid "active"
msgstr ""
-#: templates/questions.html:144
+#: forum/skins/default/templates/questions.html:111
+#: forum/skins/default/templates/questions.html:125
+msgid "Posted:"
+msgstr ""
+
+#: forum/skins/default/templates/questions.html:114
+#: forum/skins/default/templates/questions.html:119
+msgid "Updated:"
+msgstr ""
+
+#: forum/skins/default/templates/questions.html:144
msgid "Did not find anything?"
msgstr ""
-#: templates/questions.html:147
+#: forum/skins/default/templates/questions.html:147
msgid "Did not find what you were looking for?"
msgstr ""
-#: templates/questions.html:149
+#: forum/skins/default/templates/questions.html:149
msgid "Please, post your question!"
msgstr ""
-#: templates/questions.html:163
+#: forum/skins/default/templates/questions.html:163
#, python-format
msgid ""
"\n"
@@ -2466,7 +2334,7 @@ msgstr[1] ""
"<div class=\"questions-count\">%(q_num)s</div><p>questions tagged</p><div "
"class=\"tags\"><span class=\"tag\">%(tagname)s</span></div>"
-#: templates/questions.html:171
+#: forum/skins/default/templates/questions.html:171
#, python-format
msgid ""
"\n"
@@ -2480,16 +2348,14 @@ msgid_plural ""
" "
msgstr[0] ""
"\n"
-"<div class=\"questions-count\">%(q_num)s</div><p>question "
-"containing <strong><span class=\"darkred\">%(searchtitle)s</span></strong></"
-"p>"
+"<div class=\"questions-count\">%(q_num)s</div><p>question containing "
+"<strong><span class=\"darkred\">%(searchtitle)s</span></strong></p>"
msgstr[1] ""
"\n"
-"<div class=\"questions-count\">%(q_num)s</div><p>questions "
-"containing <strong><span class=\"darkred\">%(searchtitle)s</span></strong></"
-"p>"
+"<div class=\"questions-count\">%(q_num)s</div><p>questions containing "
+"<strong><span class=\"darkred\">%(searchtitle)s</span></strong></p>"
-#: templates/questions.html:177
+#: forum/skins/default/templates/questions.html:177
#, python-format
msgid ""
"\n"
@@ -2512,7 +2378,7 @@ msgstr[1] ""
"containing <strong><span class=\"darkred\">%(searchtitle)s</span></strong></"
"p>"
-#: templates/questions.html:185
+#: forum/skins/default/templates/questions.html:185
#, python-format
msgid ""
"\n"
@@ -2531,7 +2397,7 @@ msgstr[1] ""
"<div class=\"questions-count\">%(q_num)s</div><p>questions without an "
"accepted answer</p>"
-#: templates/questions.html:191
+#: forum/skins/default/templates/questions.html:191
#, python-format
msgid ""
"\n"
@@ -2548,176 +2414,182 @@ msgstr[1] ""
"\n"
"<div class=\"questions-count\">%(q_num)s</div><p>questions<p>"
-#: templates/questions.html:201
+#: forum/skins/default/templates/questions.html:201
msgid "latest questions info"
msgstr "<strong>Newest</strong> questions are shown first."
-#: templates/questions.html:205
+#: forum/skins/default/templates/questions.html:205
msgid "Questions are sorted by the <strong>time of last update</strong>."
msgstr ""
-#: templates/questions.html:206
+#: forum/skins/default/templates/questions.html:206
msgid "Most recently answered ones are shown first."
msgstr "<strong>Most recently answered</strong> questions are shown first."
-#: templates/questions.html:210
+#: forum/skins/default/templates/questions.html:210
msgid "Questions sorted by <strong>number of responses</strong>."
msgstr "Questions sorted by the <strong>number of answers</strong>."
-#: templates/questions.html:211
+#: forum/skins/default/templates/questions.html:211
msgid "Most answered questions are shown first."
msgstr " "
-#: templates/questions.html:215
+#: forum/skins/default/templates/questions.html:215
msgid "Questions are sorted by the <strong>number of votes</strong>."
msgstr ""
-#: templates/questions.html:216
+#: forum/skins/default/templates/questions.html:216
msgid "Most voted questions are shown first."
msgstr ""
-#: templates/questions.html:224 templates/unanswered.html:118
+#: forum/skins/default/templates/questions.html:224
msgid "Related tags"
msgstr "Tags"
-#: templates/questions.html:227 templates/tag_selector.html:10
-#: templates/tag_selector.html.py:27
+#: forum/skins/default/templates/questions.html:227
+#: forum/skins/default/templates/tag_selector.html:10
+#: forum/skins/default/templates/tag_selector.html:27
#, python-format
msgid "see questions tagged '%(tag_name)s'"
msgstr ""
-#: templates/reopen.html:6 templates/reopen.html.py:16
+#: forum/skins/default/templates/reopen.html:6
+#: forum/skins/default/templates/reopen.html:16
msgid "Reopen question"
msgstr ""
-#: templates/reopen.html:19
+#: forum/skins/default/templates/reopen.html:19
msgid "Open the previously closed question"
msgstr ""
-#: templates/reopen.html:22
+#: forum/skins/default/templates/reopen.html:22
msgid "The question was closed for the following reason "
msgstr ""
-#: templates/reopen.html:22
+#: forum/skins/default/templates/reopen.html:22
msgid "reason - leave blank in english"
msgstr ""
-#: templates/reopen.html:22
+#: forum/skins/default/templates/reopen.html:22
msgid "on "
msgstr ""
-#: templates/reopen.html:22
+#: forum/skins/default/templates/reopen.html:22
msgid "date closed"
msgstr ""
-#: templates/reopen.html:29
+#: forum/skins/default/templates/reopen.html:29
msgid "Reopen this question"
msgstr ""
-#: templates/revisions_answer.html:7 templates/revisions_answer.html.py:38
-#: templates/revisions_question.html:8 templates/revisions_question.html:38
+#: forum/skins/default/templates/revisions_answer.html:7
+#: forum/skins/default/templates/revisions_answer.html:38
+#: forum/skins/default/templates/revisions_question.html:8
+#: forum/skins/default/templates/revisions_question.html:38
msgid "Revision history"
msgstr ""
-#: templates/revisions_answer.html:50 templates/revisions_question.html:50
+#: forum/skins/default/templates/revisions_answer.html:50
+#: forum/skins/default/templates/revisions_question.html:50
msgid "click to hide/show revision"
msgstr ""
-#: templates/tag_selector.html:4
+#: forum/skins/default/templates/tag_selector.html:4
msgid "Interesting tags"
msgstr ""
-#: templates/tag_selector.html:14
+#: forum/skins/default/templates/tag_selector.html:14
#, python-format
msgid "remove '%(tag_name)s' from the list of interesting tags"
msgstr ""
-#: templates/tag_selector.html:20 templates/tag_selector.html.py:37
+#: forum/skins/default/templates/tag_selector.html:20
+#: forum/skins/default/templates/tag_selector.html:37
msgid "Add"
msgstr ""
-#: templates/tag_selector.html:21
+#: forum/skins/default/templates/tag_selector.html:21
msgid "Ignored tags"
msgstr ""
-#: templates/tag_selector.html:31
+#: forum/skins/default/templates/tag_selector.html:31
#, python-format
msgid "remove '%(tag_name)s' from the list of ignored tags"
msgstr ""
-#: templates/tag_selector.html:40
+#: forum/skins/default/templates/tag_selector.html:40
msgid "keep ingored questions hidden"
msgstr ""
-#: templates/tags.html:6 templates/tags.html.py:30
+#: forum/skins/default/templates/tags.html:6
+#: forum/skins/default/templates/tags.html:30
msgid "Tag list"
msgstr ""
-#: templates/tags.html:32
+#: forum/skins/default/templates/tags.html:32
msgid "sorted alphabetically"
msgstr ""
-#: templates/tags.html:32
+#: forum/skins/default/templates/tags.html:32
msgid "by name"
msgstr ""
-#: templates/tags.html:33
+#: forum/skins/default/templates/tags.html:33
msgid "sorted by frequency of tag use"
msgstr ""
-#: templates/tags.html:33
+#: forum/skins/default/templates/tags.html:33
msgid "by popularity"
msgstr ""
-#: templates/tags.html:39
+#: forum/skins/default/templates/tags.html:39
msgid "All tags matching query"
msgstr ""
-#: templates/tags.html:39
+#: forum/skins/default/templates/tags.html:39
msgid "all tags - make this empty in english"
msgstr ""
-#: templates/tags.html:42
+#: forum/skins/default/templates/tags.html:42
msgid "Nothing found"
msgstr ""
-#: templates/unanswered.html:114
-#, python-format
-msgid "have %(num_q)s unanswered questions"
-msgstr ""
-"<div class=\"questions-count\">%(num_q)s</div>questions <strong>without "
-"accepted answers</strong>"
-
-#: templates/user_edit.html:6
+#: forum/skins/default/templates/user_edit.html:6
msgid "Edit user profile"
msgstr ""
-#: templates/user_edit.html:19
+#: forum/skins/default/templates/user_edit.html:19
msgid "edit profile"
msgstr ""
-#: templates/user_edit.html:31
+#: forum/skins/default/templates/user_edit.html:31
msgid "image associated with your email address"
msgstr ""
-#: templates/user_edit.html:31
+#: forum/skins/default/templates/user_edit.html:31
#, python-format
msgid "avatar, see %(gravatar_faq_url)s"
msgstr "<a href='%(gravatar_faq_url)s'>gravatar</a>"
-#: templates/user_edit.html:36 templates/user_info.html:56
+#: forum/skins/default/templates/user_edit.html:36
+#: forum/skins/default/templates/user_info.html:56
msgid "Registered user"
msgstr ""
-#: templates/user_edit.html:86 templates/user_email_subscriptions.html:20
+#: forum/skins/default/templates/user_edit.html:43
+msgid "Screen Name"
+msgstr ""
+
+#: forum/skins/default/templates/user_edit.html:86
+#: forum/skins/default/templates/user_email_subscriptions.html:20
msgid "Update"
msgstr ""
-#: templates/user_email_subscriptions.html:8
+#: forum/skins/default/templates/user_email_subscriptions.html:8
msgid "Email subscription settings"
msgstr ""
-#: templates/user_email_subscriptions.html:9
+#: forum/skins/default/templates/user_email_subscriptions.html:9
msgid "email subscription settings info"
msgstr ""
"<span class='big strong'>Adjust frequency of email updates.</span> Receive "
@@ -2726,55 +2598,60 @@ msgstr ""
"receive emails - select 'no email' on all items below.<br/>Updates are only "
"sent when there is any new activity on selected items."
-#: templates/user_email_subscriptions.html:21
+#: forum/skins/default/templates/user_email_subscriptions.html:21
msgid "Stop sending email"
msgstr "Stop Email"
-#: templates/user_info.html:32
+#: forum/skins/default/templates/user_info.html:22
+#: forum/skins/default/templates/users.html:26 forum/views/users.py:907
+msgid "reputation"
+msgstr "karma"
+
+#: forum/skins/default/templates/user_info.html:32
msgid "Moderate this user"
msgstr ""
-#: templates/user_info.html:45
+#: forum/skins/default/templates/user_info.html:45
msgid "update profile"
msgstr ""
-#: templates/user_info.html:60
+#: forum/skins/default/templates/user_info.html:60
msgid "real name"
msgstr ""
-#: templates/user_info.html:65
+#: forum/skins/default/templates/user_info.html:65
msgid "member for"
msgstr "member since"
-#: templates/user_info.html:70
+#: forum/skins/default/templates/user_info.html:70
msgid "last seen"
msgstr ""
-#: templates/user_info.html:76
+#: forum/skins/default/templates/user_info.html:76
msgid "user website"
msgstr ""
-#: templates/user_info.html:82
+#: forum/skins/default/templates/user_info.html:82
msgid "location"
msgstr ""
-#: templates/user_info.html:89
+#: forum/skins/default/templates/user_info.html:89
msgid "age"
msgstr ""
-#: templates/user_info.html:90
+#: forum/skins/default/templates/user_info.html:90
msgid "age unit"
msgstr "years old"
-#: templates/user_info.html:96
+#: forum/skins/default/templates/user_info.html:96
msgid "todays unused votes"
msgstr ""
-#: templates/user_info.html:97
+#: forum/skins/default/templates/user_info.html:97
msgid "votes left"
msgstr ""
-#: templates/user_stats.html:12
+#: forum/skins/default/templates/user_stats.html:12
#, python-format
msgid ""
"\n"
@@ -2787,7 +2664,7 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/user_stats.html:23
+#: forum/skins/default/templates/user_stats.html:23
#, python-format
msgid ""
"\n"
@@ -2800,16 +2677,16 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/user_stats.html:36
+#: forum/skins/default/templates/user_stats.html:36
#, python-format
msgid "the answer has been voted for %(vote_count)s times"
msgstr ""
-#: templates/user_stats.html:36
+#: forum/skins/default/templates/user_stats.html:36
msgid "this answer has been selected as correct"
msgstr ""
-#: templates/user_stats.html:46
+#: forum/skins/default/templates/user_stats.html:46
#, python-format
msgid ""
"\n"
@@ -2826,7 +2703,7 @@ msgstr[1] ""
"\n"
"(%(comment_count)s comments)"
-#: templates/user_stats.html:61
+#: forum/skins/default/templates/user_stats.html:61
#, python-format
msgid ""
"\n"
@@ -2839,23 +2716,23 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/user_stats.html:72
+#: forum/skins/default/templates/user_stats.html:72
msgid "thumb up"
msgstr ""
-#: templates/user_stats.html:73
+#: forum/skins/default/templates/user_stats.html:73
msgid "user has voted up this many times"
msgstr ""
-#: templates/user_stats.html:77
+#: forum/skins/default/templates/user_stats.html:77
msgid "thumb down"
msgstr ""
-#: templates/user_stats.html:78
+#: forum/skins/default/templates/user_stats.html:78
msgid "user voted down this many times"
msgstr ""
-#: templates/user_stats.html:87
+#: forum/skins/default/templates/user_stats.html:87
#, python-format
msgid ""
"\n"
@@ -2868,13 +2745,13 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/user_stats.html:100
+#: forum/skins/default/templates/user_stats.html:100
#, python-format
msgid ""
"see other questions with %(view_user)s's contributions tagged '%(tag_name)s' "
msgstr ""
-#: templates/user_stats.html:115
+#: forum/skins/default/templates/user_stats.html:115
#, python-format
msgid ""
"\n"
@@ -2887,78 +2764,107 @@ msgid_plural ""
msgstr[0] ""
msgstr[1] ""
-#: templates/user_tabs.html:7
+#: forum/skins/default/templates/user_tabs.html:7
msgid "User profile"
msgstr ""
-#: templates/user_tabs.html:16
+#: forum/skins/default/templates/user_tabs.html:7 forum/views/users.py:881
+msgid "overview"
+msgstr ""
+
+#: forum/skins/default/templates/user_tabs.html:9 forum/views/users.py:889
+msgid "recent activity"
+msgstr ""
+
+#: forum/skins/default/templates/user_tabs.html:12 forum/views/users.py:899
+msgid "comments and answers to others questions"
+msgstr ""
+
+#: forum/skins/default/templates/user_tabs.html:13 forum/views/users.py:898
+msgid "responses"
+msgstr ""
+
+#: forum/skins/default/templates/user_tabs.html:16
msgid "graph of user reputation"
msgstr "Graph of user karma"
-#: templates/user_tabs.html:17
+#: forum/skins/default/templates/user_tabs.html:17
msgid "reputation history"
msgstr "karma history"
-#: templates/user_tabs.html:23
+#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:925
+msgid "user vote record"
+msgstr ""
+
+#: forum/skins/default/templates/user_tabs.html:20 forum/views/users.py:924
+msgid "casted votes"
+msgstr "votes"
+
+#: forum/skins/default/templates/user_tabs.html:23
msgid "questions that user selected as his/her favorite"
msgstr ""
-#: templates/user_tabs.html:24
+#: forum/skins/default/templates/user_tabs.html:24
msgid "favorites"
msgstr ""
-#: templates/users.html:6 templates/users.html.py:24
+#: forum/skins/default/templates/user_tabs.html:27 forum/views/users.py:934
+msgid "email subscription settings"
+msgstr ""
+
+#: forum/skins/default/templates/user_tabs.html:28 forum/views/users.py:933
+msgid "email subscriptions"
+msgstr ""
+
+#: forum/skins/default/templates/users.html:6
+#: forum/skins/default/templates/users.html:24
msgid "Users"
msgstr ""
-#: templates/users.html:27
+#: forum/skins/default/templates/users.html:27
msgid "recent"
msgstr ""
-#: templates/users.html:28
+#: forum/skins/default/templates/users.html:28
msgid "oldest"
msgstr ""
-#: templates/users.html:29
+#: forum/skins/default/templates/users.html:29
msgid "by username"
msgstr ""
-#: templates/users.html:35
+#: forum/skins/default/templates/users.html:35
#, python-format
msgid "users matching query %(suser)s:"
msgstr ""
-#: templates/users.html:39
+#: forum/skins/default/templates/users.html:39
msgid "Nothing found."
msgstr ""
-#: templates/users_questions.html:11
+#: forum/skins/default/templates/users_questions.html:11
msgid "this questions was selected as favorite"
msgstr ""
-#: templates/users_questions.html:12
+#: forum/skins/default/templates/users_questions.html:12
msgid "thumb-up on"
msgstr ""
-#: templates/users_questions.html:19
+#: forum/skins/default/templates/users_questions.html:19
msgid "thumb-up off"
msgstr ""
-#: templates/users_questions.html:34
-msgid "this answer has been accepted to be correct"
-msgstr ""
-
-#: templates/authopenid/changeemail.html:3
-#: templates/authopenid/changeemail.html:9
-#: templates/authopenid/changeemail.html:38
+#: forum/skins/default/templates/authopenid/changeemail.html:3
+#: forum/skins/default/templates/authopenid/changeemail.html:9
+#: forum/skins/default/templates/authopenid/changeemail.html:38
msgid "Change email"
msgstr "Change Email"
-#: templates/authopenid/changeemail.html:11
+#: forum/skins/default/templates/authopenid/changeemail.html:11
msgid "Save your email address"
msgstr ""
-#: templates/authopenid/changeemail.html:16
+#: forum/skins/default/templates/authopenid/changeemail.html:16
#, python-format
msgid "change %(email)s info"
msgstr ""
@@ -2966,7 +2872,7 @@ msgstr ""
"you'd like to use another email for <strong>update subscriptions</strong>."
"<br>Currently you are using <strong>%(email)s</strong>"
-#: templates/authopenid/changeemail.html:18
+#: forum/skins/default/templates/authopenid/changeemail.html:18
#, python-format
msgid "here is why email is required, see %(gravatar_faq_url)s"
msgstr ""
@@ -2977,26 +2883,26 @@ msgstr ""
"(gravatar_faq_url)s'><strong>gravatar</strong></a> image for your account. "
"Email addresses are never shown or otherwise shared with anybody else."
-#: templates/authopenid/changeemail.html:31
+#: forum/skins/default/templates/authopenid/changeemail.html:31
msgid "Your new Email"
msgstr ""
"<strong>Your new Email:</strong> (will <strong>not</strong> be shown to "
"anyone, must be valid)"
-#: templates/authopenid/changeemail.html:31
+#: forum/skins/default/templates/authopenid/changeemail.html:31
msgid "Your Email"
msgstr ""
"<strong>Your Email</strong> (<i>must be valid, never shown to others</i>)"
-#: templates/authopenid/changeemail.html:38
+#: forum/skins/default/templates/authopenid/changeemail.html:38
msgid "Save Email"
msgstr ""
-#: templates/authopenid/changeemail.html:49
+#: forum/skins/default/templates/authopenid/changeemail.html:49
msgid "Validate email"
msgstr ""
-#: templates/authopenid/changeemail.html:52
+#: forum/skins/default/templates/authopenid/changeemail.html:52
#, python-format
msgid "validate %(email)s info or go to %(change_email_url)s"
msgstr ""
@@ -3007,11 +2913,11 @@ msgstr ""
"<strong>another email</strong>, please <a href='%(change_email_url)"
"s'><strong>change it again</strong></a>."
-#: templates/authopenid/changeemail.html:57
+#: forum/skins/default/templates/authopenid/changeemail.html:57
msgid "Email not changed"
msgstr ""
-#: templates/authopenid/changeemail.html:60
+#: forum/skins/default/templates/authopenid/changeemail.html:60
#, python-format
msgid "old %(email)s kept, if you like go to %(change_email_url)s"
msgstr ""
@@ -3020,11 +2926,11 @@ msgstr ""
"it in your user profile or by using the <a href='%(change_email_url)"
"s'><strong>previous form</strong></a> again."
-#: templates/authopenid/changeemail.html:65
+#: forum/skins/default/templates/authopenid/changeemail.html:65
msgid "Email changed"
msgstr ""
-#: templates/authopenid/changeemail.html:68
+#: forum/skins/default/templates/authopenid/changeemail.html:68
#, python-format
msgid "your current %(email)s can be used for this"
msgstr ""
@@ -3033,11 +2939,11 @@ msgstr ""
"Email notifications are sent once a day or less frequently - only when there "
"are any news."
-#: templates/authopenid/changeemail.html:73
+#: forum/skins/default/templates/authopenid/changeemail.html:73
msgid "Email verified"
msgstr ""
-#: templates/authopenid/changeemail.html:76
+#: forum/skins/default/templates/authopenid/changeemail.html:76
msgid "thanks for verifying email"
msgstr ""
"<span class=\"big strong\">Thank you for verifying your email!</span> Now "
@@ -3046,11 +2952,11 @@ msgstr ""
"updates</strong> - then will be notified about changes <strong>once a day</"
"strong> or less frequently."
-#: templates/authopenid/changeemail.html:81
+#: forum/skins/default/templates/authopenid/changeemail.html:81
msgid "email key not sent"
msgstr "Validation email not sent"
-#: templates/authopenid/changeemail.html:84
+#: forum/skins/default/templates/authopenid/changeemail.html:84
#, python-format
msgid "email key not sent %(email)s change email here %(change_link)s"
msgstr ""
@@ -3058,54 +2964,56 @@ 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."
-#: templates/authopenid/changeopenid.html:4
-#: templates/authopenid/changeopenid.html:30
-#: templates/authopenid/settings.html:34
+#: 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 ""
-#: templates/authopenid/changeopenid.html:8
+#: forum/skins/default/templates/authopenid/changeopenid.html:8
msgid "Account: change OpenID URL"
msgstr ""
-#: templates/authopenid/changeopenid.html:12
+#: forum/skins/default/templates/authopenid/changeopenid.html:12
msgid ""
"This is where you can change your OpenID URL. Make sure you remember it!"
msgstr ""
-#: templates/authopenid/changeopenid.html:14
-#: templates/authopenid/delete.html:14 templates/authopenid/delete.html:24
+#: forum/skins/default/templates/authopenid/changeopenid.html:14
+#: forum/skins/default/templates/authopenid/delete.html:14
+#: forum/skins/default/templates/authopenid/delete.html:24
msgid "Please correct errors below:"
msgstr ""
-#: templates/authopenid/changeopenid.html:29
+#: forum/skins/default/templates/authopenid/changeopenid.html:29
msgid "OpenID URL:"
msgstr ""
-#: templates/authopenid/changepw.html:5 templates/authopenid/changepw.html:14
-#: templates/authopenid/settings.html:29
+#: 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 ""
-#: templates/authopenid/changepw.html:7
+#: forum/skins/default/templates/authopenid/changepw.html:7
msgid "Account: change password"
msgstr "Change your password"
-#: templates/authopenid/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"
-#: templates/authopenid/complete.html:19
+#: forum/skins/default/templates/authopenid/complete.html:19
msgid "Connect your OpenID with this site"
msgstr "New user signup"
-#: templates/authopenid/complete.html:22
+#: forum/skins/default/templates/authopenid/complete.html:22
msgid "Connect your OpenID with your account on this site"
msgstr "New user signup"
-#: templates/authopenid/complete.html:27
+#: forum/skins/default/templates/authopenid/complete.html:27
#, python-format
msgid "register new %(provider)s account info, see %(gravatar_faq_url)s"
msgstr ""
@@ -3116,7 +3024,7 @@ msgstr ""
"questions and will be used to create and retrieve your unique avatar image - "
"<a href='%(gravatar_faq_url)s'><strong>gravatar</strong></a>.</p>"
-#: templates/authopenid/complete.html:31
+#: forum/skins/default/templates/authopenid/complete.html:31
#, python-format
msgid ""
"%(username)s already exists, choose another name for \n"
@@ -3133,7 +3041,7 @@ msgstr ""
"updates</strong> on the interesting questions or entire forum by email. "
"Email addresses are never shown or otherwise shared with anybody else.</p>"
-#: templates/authopenid/complete.html:35
+#: forum/skins/default/templates/authopenid/complete.html:35
#, python-format
msgid ""
"register new external %(provider)s account info, see %(gravatar_faq_url)s"
@@ -3147,25 +3055,37 @@ msgstr ""
"retrieve your unique avatar image - <a href='%(gravatar_faq_url)"
"s'><strong>gravatar</strong></a>.</p>"
-#: templates/authopenid/complete.html:40
+#: forum/skins/default/templates/authopenid/complete.html:38
+#, python-format
+msgid "register new Facebook connect account info, see %(gravatar_faq_url)s"
+msgstr ""
+"<p><span class=\"big strong\">You are here for the first time with your "
+"Facebook login.</span> Please create your <strong>screen name</strong> and "
+"save your <strong>email</strong> address. Saved email address will let you "
+"<strong>subscribe for the updates</strong> on the most interesting questions "
+"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 ""
-#: templates/authopenid/complete.html:55
+#: forum/skins/default/templates/authopenid/complete.html:57
msgid "Sorry, looks like we have some errors:"
msgstr ""
-#: templates/authopenid/complete.html:76
+#: forum/skins/default/templates/authopenid/complete.html:82
msgid "Screen name label"
msgstr "<strong>Screen Name</strong> (<i>will be shown to others</i>)"
-#: templates/authopenid/complete.html:83
+#: 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>)"
-#: templates/authopenid/complete.html:89 templates/authopenid/signup.html:15
+#: 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 "
@@ -3174,58 +3094,64 @@ msgstr ""
"week</strong> - only when there is anything new.<br/>If you like, please "
"adjust this now or any time later from your user account."
-#: templates/authopenid/complete.html:91
+#: 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 ""
-#: templates/authopenid/complete.html:92
+#: forum/skins/default/templates/authopenid/complete.html:103
msgid "create account"
msgstr "Signup"
-#: templates/authopenid/complete.html:101
+#: forum/skins/default/templates/authopenid/complete.html:112
msgid "Existing account"
msgstr ""
-#: templates/authopenid/complete.html:102
+#: forum/skins/default/templates/authopenid/complete.html:113
msgid "user name"
msgstr ""
-#: templates/authopenid/complete.html:103
+#: forum/skins/default/templates/authopenid/complete.html:114
msgid "password"
msgstr ""
-#: templates/authopenid/complete.html:108
+#: forum/skins/default/templates/authopenid/complete.html:121
msgid "Register"
msgstr ""
-#: templates/authopenid/complete.html:109 templates/authopenid/signin.html:140
+#: forum/skins/default/templates/authopenid/complete.html:122
+#: forum/skins/default/templates/authopenid/signin.html:151
msgid "Forgot your password?"
msgstr ""
-#: templates/authopenid/confirm_email.txt:2
+#: forum/skins/default/templates/authopenid/confirm_email.txt:2
msgid "Thank you for registering at our Q&A forum!"
msgstr ""
-#: templates/authopenid/confirm_email.txt:4
+#: forum/skins/default/templates/authopenid/confirm_email.txt:4
msgid "Your account details are:"
msgstr ""
-#: templates/authopenid/confirm_email.txt:6
+#: forum/skins/default/templates/authopenid/confirm_email.txt:6
msgid "Username:"
msgstr ""
-#: templates/authopenid/confirm_email.txt:7
-#: templates/authopenid/delete.html:19
+#: forum/skins/default/templates/authopenid/confirm_email.txt:7
+#: forum/skins/default/templates/authopenid/delete.html:19
msgid "Password:"
msgstr ""
-#: templates/authopenid/confirm_email.txt:9
+#: forum/skins/default/templates/authopenid/confirm_email.txt:9
msgid "Please sign in here:"
msgstr ""
-#: templates/authopenid/confirm_email.txt:12
-#: templates/authopenid/email_validation.txt:14
-#: templates/authopenid/sendpw_email.txt:8
+#: forum/skins/default/templates/authopenid/confirm_email.txt:12
+#: forum/skins/default/templates/authopenid/email_validation.txt:14
+#: forum/skins/default/templates/authopenid/sendpw_email.txt:8
msgid ""
"Sincerely,\n"
"Forum Administrator"
@@ -3233,73 +3159,78 @@ msgstr ""
"Sincerely,\n"
"Q&A Forum Administrator"
-#: templates/authopenid/delete.html:4 templates/authopenid/settings.html:38
+#: forum/skins/default/templates/authopenid/delete.html:4
+#: forum/skins/default/templates/authopenid/settings.html:38
msgid "Delete account"
msgstr ""
-#: templates/authopenid/delete.html:8
+#: forum/skins/default/templates/authopenid/delete.html:8
msgid "Account: delete account"
msgstr ""
-#: templates/authopenid/delete.html:12
+#: forum/skins/default/templates/authopenid/delete.html:12
msgid ""
"Note: After deleting your account, anyone will be able to register this "
"username."
msgstr ""
-#: templates/authopenid/delete.html:16
+#: forum/skins/default/templates/authopenid/delete.html:16
msgid "Check confirm box, if you want delete your account."
msgstr ""
-#: templates/authopenid/delete.html:31
+#: forum/skins/default/templates/authopenid/delete.html:31
msgid "I am sure I want to delete my account."
msgstr ""
-#: templates/authopenid/delete.html:32
+#: forum/skins/default/templates/authopenid/delete.html:32
msgid "Password/OpenID URL"
msgstr ""
-#: templates/authopenid/delete.html:32
+#: forum/skins/default/templates/authopenid/delete.html:32
msgid "(required for your security)"
msgstr ""
-#: templates/authopenid/delete.html:34
+#: forum/skins/default/templates/authopenid/delete.html:34
msgid "Delete account permanently"
msgstr ""
-#: templates/authopenid/email_validation.txt:2
+#: forum/skins/default/templates/authopenid/email_validation.txt:2
msgid "Greetings from the Q&A forum"
msgstr ""
-#: templates/authopenid/email_validation.txt:4
+#: forum/skins/default/templates/authopenid/email_validation.txt:4
msgid "To make use of the Forum, please follow the link below:"
msgstr ""
-#: templates/authopenid/email_validation.txt:8
+#: forum/skins/default/templates/authopenid/email_validation.txt:8
msgid "Following the link above will help us verify your email address."
msgstr ""
-#: templates/authopenid/email_validation.txt:10
+#: forum/skins/default/templates/authopenid/email_validation.txt:10
msgid ""
"If you beleive that this message was sent in mistake - \n"
"no further action is needed. Just ingore this email, we apologize\n"
"for any inconvenience"
msgstr ""
-#: templates/authopenid/external_legacy_login_info.html:4
-#: templates/authopenid/external_legacy_login_info.html:7
+#: forum/skins/default/templates/authopenid/external_legacy_login_info.html:4
+#: forum/skins/default/templates/authopenid/external_legacy_login_info.html:7
msgid "Traditional login information"
msgstr ""
-#: templates/authopenid/external_legacy_login_info.html:12
-msgid "how to login with password through external login website"
+#: forum/skins/default/templates/authopenid/external_legacy_login_info.html:12
+#, python-format
+msgid ""
+"how to login with password through external login website or use %"
+"(feedback_url)s"
msgstr ""
-#: templates/authopenid/sendpw.html:4 templates/authopenid/sendpw.html.py:7
+#: forum/skins/default/templates/authopenid/sendpw.html:4
+#: forum/skins/default/templates/authopenid/sendpw.html:7
msgid "Send new password"
msgstr "Recover password"
-#: templates/authopenid/sendpw.html:10
+#: forum/skins/default/templates/authopenid/sendpw.html:10
msgid "password recovery information"
msgstr ""
"<span class='big strong'>Forgot you password? No problems - just get a new "
@@ -3309,22 +3240,22 @@ msgstr ""
"login with the suggested password<br/>&bull; at this you might want to "
"change your password to something you can remember better"
-#: templates/authopenid/sendpw.html:21
+#: forum/skins/default/templates/authopenid/sendpw.html:21
msgid "Reset password"
msgstr "Send me a new password"
-#: templates/authopenid/sendpw.html:22
+#: forum/skins/default/templates/authopenid/sendpw.html:22
msgid "return to login"
msgstr ""
-#: templates/authopenid/sendpw_email.txt:2
+#: forum/skins/default/templates/authopenid/sendpw_email.txt:2
#, python-format
msgid ""
"Someone has requested to reset your password on %(site_url)s.\n"
"If it were not you, it is safe to ignore this email."
msgstr ""
-#: templates/authopenid/sendpw_email.txt:5
+#: forum/skins/default/templates/authopenid/sendpw_email.txt:5
#, python-format
msgid ""
"email explanation how to use new %(password)s for %(username)s\n"
@@ -3335,35 +3266,36 @@ 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"
-#: templates/authopenid/settings.html:4
+#: forum/skins/default/templates/authopenid/settings.html:4
msgid "Account functions"
msgstr ""
-#: templates/authopenid/settings.html:30
+#: forum/skins/default/templates/authopenid/settings.html:30
msgid "Give your account a new password."
msgstr ""
-#: templates/authopenid/settings.html:31
+#: forum/skins/default/templates/authopenid/settings.html:31
msgid "Change email "
msgstr ""
-#: templates/authopenid/settings.html:32
+#: forum/skins/default/templates/authopenid/settings.html:32
msgid "Add or update the email address associated with your account."
msgstr ""
-#: templates/authopenid/settings.html:35
+#: forum/skins/default/templates/authopenid/settings.html:35
msgid "Change openid associated to your account"
msgstr ""
-#: templates/authopenid/settings.html:39
+#: forum/skins/default/templates/authopenid/settings.html:39
msgid "Erase your username and all your data from website"
msgstr ""
-#: templates/authopenid/signin.html:5 templates/authopenid/signin.html:21
+#: forum/skins/default/templates/authopenid/signin.html:5
+#: forum/skins/default/templates/authopenid/signin.html:21
msgid "User login"
msgstr "User login"
-#: templates/authopenid/signin.html:28
+#: forum/skins/default/templates/authopenid/signin.html:28
#, python-format
msgid ""
"\n"
@@ -3376,7 +3308,7 @@ msgstr ""
"strong> %(summary)s...\"</i> <span class=\"strong big\">is saved and will be "
"posted once you log in.</span>"
-#: templates/authopenid/signin.html:35
+#: forum/skins/default/templates/authopenid/signin.html:35
#, python-format
msgid ""
"Your question \n"
@@ -3387,7 +3319,7 @@ msgstr ""
"strong> %(summary)s...\"</i> <span class=\"strong big\">is saved and will be "
"posted once you log in.</span>"
-#: templates/authopenid/signin.html:42
+#: 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."
@@ -3397,14 +3329,14 @@ msgstr ""
"have to remember another one. CNPROG option requires your login name and "
"password entered here.</font></p>"
-#: templates/authopenid/signin.html:117
+#: forum/skins/default/templates/authopenid/signin.html:128
msgid "Enter your <span id=\"enter_your_what\">Provider user name</span>"
msgstr ""
"<span class=\"big strong\">Enter your </span><span id=\"enter_your_what\" "
"class='big strong'>Provider user name</span><br/><span class='grey'>(or "
"select another login method above)</span>"
-#: templates/authopenid/signin.html:124
+#: forum/skins/default/templates/authopenid/signin.html:135
msgid ""
"Enter your <a class=\"openid_logo\" href=\"http://openid.net\">OpenID</a> "
"web address"
@@ -3413,67 +3345,68 @@ msgstr ""
"openid.net\">OpenID</a> web address</span><br/><span class='grey'>(or choose "
"another login method above)</span>"
-#: templates/authopenid/signin.html:126 templates/authopenid/signin.html:138
+#: forum/skins/default/templates/authopenid/signin.html:137
+#: forum/skins/default/templates/authopenid/signin.html:149
msgid "Login"
msgstr ""
-#: templates/authopenid/signin.html:129
+#: forum/skins/default/templates/authopenid/signin.html:140
msgid "Enter your login name and password"
msgstr ""
"<span class='big strong'>Enter your CNPROG login and password</span><br/"
"><span class='grey'>(or select your OpenID provider above)</span>"
-#: templates/authopenid/signin.html:133
+#: forum/skins/default/templates/authopenid/signin.html:144
msgid "Login name"
msgstr ""
-#: templates/authopenid/signin.html:135
+#: forum/skins/default/templates/authopenid/signin.html:146
msgid "Password"
msgstr ""
-#: templates/authopenid/signin.html:139
+#: forum/skins/default/templates/authopenid/signin.html:150
msgid "Create account"
msgstr ""
-#: templates/authopenid/signin.html:149
+#: forum/skins/default/templates/authopenid/signin.html:160
msgid "Why use OpenID?"
msgstr ""
-#: templates/authopenid/signin.html:152
+#: 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."
-#: templates/authopenid/signin.html:155
+#: 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."
-#: templates/authopenid/signin.html:158
+#: 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."
-#: templates/authopenid/signin.html:161
+#: 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."
-#: templates/authopenid/signin.html:166
+#: forum/skins/default/templates/authopenid/signin.html:177
msgid "Find out more"
msgstr ""
-#: templates/authopenid/signin.html:167
+#: forum/skins/default/templates/authopenid/signin.html:178
msgid "Get OpenID"
msgstr ""
-#: templates/authopenid/signup.html:4
+#: forum/skins/default/templates/authopenid/signup.html:4
msgid "Signup"
msgstr ""
-#: templates/authopenid/signup.html:8
+#: forum/skins/default/templates/authopenid/signup.html:8
msgid "Create login name and password"
msgstr ""
-#: templates/authopenid/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 "
@@ -3482,14 +3415,227 @@ msgstr ""
"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."
-#: templates/authopenid/signup.html:17
+#: 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 ""
-#: templates/authopenid/signup.html:19
+#: forum/skins/default/templates/authopenid/signup.html:29
msgid "return to OpenID login"
msgstr ""
+#: forum/skins/default/templates/fbconnect/xd_receiver.html:5
+#, python-format
+msgid "Connect to %(APP_SHORT_NAME)s with Facebook!"
+msgstr ""
+
+#: forum/templatetags/extra_tags.py:166 forum/templatetags/extra_tags.py:193
+msgid "reputation points"
+msgstr "karma"
+
+#: forum/templatetags/extra_tags.py:253
+msgid "2 days ago"
+msgstr ""
+
+#: forum/templatetags/extra_tags.py:255
+msgid "yesterday"
+msgstr ""
+
+#: forum/templatetags/extra_tags.py:257
+#, python-format
+msgid "%(hr)d hour ago"
+msgid_plural "%(hr)d hours ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: forum/templatetags/extra_tags.py:259
+#, python-format
+msgid "%(min)d min ago"
+msgid_plural "%(min)d mins ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: forum/utils/forms.py:27
+msgid "this field is required"
+msgstr ""
+
+#: forum/utils/forms.py:42
+msgid "choose a username"
+msgstr "Choose screen name"
+
+#: forum/utils/forms.py:47
+msgid "user name is required"
+msgstr ""
+
+#: forum/utils/forms.py:48
+msgid "sorry, this name is taken, please choose another"
+msgstr ""
+
+#: forum/utils/forms.py:49
+msgid "sorry, this name is not allowed, please choose another"
+msgstr ""
+
+#: forum/utils/forms.py:50
+msgid "sorry, there is no user with this name"
+msgstr ""
+
+#: forum/utils/forms.py:51
+msgid "sorry, we have a serious error - user name is taken by several users"
+msgstr ""
+
+#: forum/utils/forms.py:52
+msgid "user name can only consist of letters, empty space and underscore"
+msgstr ""
+
+#: forum/utils/forms.py:100
+msgid "your email address"
+msgstr "Your email <i>(never shared)</i>"
+
+#: forum/utils/forms.py:101
+msgid "email address is required"
+msgstr ""
+
+#: forum/utils/forms.py:102
+msgid "please enter a valid email address"
+msgstr ""
+
+#: forum/utils/forms.py:103
+msgid "this email is already used by someone else, please choose another"
+msgstr ""
+
+#: forum/utils/forms.py:128
+msgid "choose password"
+msgstr "Password"
+
+#: forum/utils/forms.py:129
+msgid "password is required"
+msgstr ""
+
+#: forum/utils/forms.py:132
+msgid "retype password"
+msgstr "Password <i>(please retype)</i>"
+
+#: forum/utils/forms.py:133
+msgid "please, retype your password"
+msgstr ""
+
+#: forum/utils/forms.py:134
+msgid "sorry, entered passwords did not match, please try again"
+msgstr ""
+
+#: forum/views/commands.py:217
+#, python-format
+msgid "subscription saved, %(email)s needs validation, see %(details_url)s"
+msgstr ""
+"Your subscription is saved, but email address %(email)s needs to be "
+"validated, please see <a href='%(details_url)s'>more details here</a>"
+
+#: forum/views/commands.py:225
+msgid "email update frequency has been set to daily"
+msgstr ""
+
+#: forum/views/meta.py:34
+msgid "Q&A forum feedback"
+msgstr ""
+
+#: forum/views/meta.py:35
+msgid "Thanks for the feedback!"
+msgstr ""
+
+#: forum/views/meta.py:43
+msgid "We look forward to hearing your feedback! Please, give it next time :)"
+msgstr ""
+
+#: forum/views/users.py:842 forum/views/users.py:846
+msgid "changes saved"
+msgstr ""
+
+#: forum/views/users.py:852
+msgid "email updates canceled"
+msgstr ""
+
+#: forum/views/users.py:882
+msgid "user profile"
+msgstr ""
+
+#: forum/views/users.py:883
+msgid "user profile overview"
+msgstr ""
+
+#: forum/views/users.py:890
+msgid "recent user activity"
+msgstr ""
+
+#: forum/views/users.py:891
+msgid "profile - recent activity"
+msgstr ""
+
+#: forum/views/users.py:900
+msgid "profile - responses"
+msgstr ""
+
+#: forum/views/users.py:908
+msgid "user reputation in the community"
+msgstr "user karma"
+
+#: forum/views/users.py:909
+msgid "profile - user reputation"
+msgstr "Profile - User's Karma"
+
+#: forum/views/users.py:915
+msgid "favorite questions"
+msgstr ""
+
+#: forum/views/users.py:916
+msgid "users favorite questions"
+msgstr ""
+
+#: forum/views/users.py:917
+msgid "profile - favorite questions"
+msgstr ""
+
+#: forum/views/users.py:926
+msgid "profile - votes"
+msgstr ""
+
+#: forum/views/users.py:935
+msgid "profile - email subscriptions"
+msgstr ""
+
+#: forum/views/writers.py:74
+msgid "uploading images is limited to users with >60 reputation points"
+msgstr "sorry, file uploading requires karma >60"
+
+#: forum/views/writers.py:76
+msgid "allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"
+msgstr ""
+
+#: forum/views/writers.py:78
+#, python-format
+msgid "maximum upload file size is %sK"
+msgstr ""
+
+#: forum/views/writers.py:80
+#, python-format
+msgid ""
+"Error uploading file. Please contact the site administrator. Thank you. %s"
+msgstr ""
+
+#: forum_modules/books/urls.py:7 forum_modules/books/urls.py:8
+#: forum_modules/books/urls.py:9
+msgid "books/"
+msgstr ""
+
+#~ msgid "have %(num_q)s unanswered questions"
+#~ msgstr ""
+#~ "<div class=\"questions-count\">%(num_q)s</div>questions <strong>without "
+#~ "accepted answers</strong>"
+
#~ msgid ""
#~ "\n"
#~ "\t\t\t\thave total %(q_num)s questions\n"
diff --git a/locale/es/LC_MESSAGES/django.mo b/locale/es/LC_MESSAGES/django.mo
index fc7ebe14..2b514069 100644
--- a/locale/es/LC_MESSAGES/django.mo
+++ b/locale/es/LC_MESSAGES/django.mo
Binary files differ
diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po
index b528fcf2..83ff69bf 100644
--- a/locale/es/LC_MESSAGES/django.po
+++ b/locale/es/LC_MESSAGES/django.po
@@ -1,1179 +1,1453 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: \n"
+"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-08-12 15:53+0000\n"
-"PO-Revision-Date: \n"
-"Last-Translator: Bruno Sarlo <bsarlo@gmail.com>\n"
+"POT-Creation-Date: 2010-02-09 20:10+0000\n"
+"PO-Revision-Date: 2010-02-09 14:11-0600\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: settings.py:12 urls.py:25 forum/views.py:304 forum/views.py:698
-msgid "account/"
-msgstr "cuenta/"
-
-#: settings.py:12 urls.py:26 django_authopenid/urls.py:9
-#: django_authopenid/urls.py:10 django_authopenid/urls.py:11
-#: django_authopenid/urls.py:13 forum/views.py:304 forum/views.py:699
-#: templates/authopenid/confirm_email.txt:10
-msgid "signin/"
-msgstr "ingresar/"
-
-#: urls.py:22
-msgid "upfiles/"
-msgstr "archivossubidos/"
-
-#: urls.py:27 urls.py:28 urls.py:29 django_authopenid/urls.py:26
-#: django_authopenid/urls.py:27
-msgid "email/"
-msgstr "email/"
-
-#: urls.py:27
-msgid "change/"
-msgstr "cambiar/"
-
-#: urls.py:28
-msgid "sendkey/"
-msgstr "enviarclave/"
-
-#: urls.py:29
-msgid "verify/"
-msgstr "verificar/"
-
-#: urls.py:30
-msgid "about/"
-msgstr "acercadenosotros/"
-
-#: urls.py:31
-msgid "faq/"
-msgstr "preguntasfrecuentes/"
-
-#: urls.py:32
-msgid "privacy/"
-msgstr "códigodeprivacidad/"
-
-#: urls.py:33
-msgid "logout/"
-msgstr "cerrarsesion/"
-
-#: urls.py:34 urls.py:35 urls.py:36 urls.py:48 forum/models.py:418
-msgid "answers/"
-msgstr "respuestas/"
-
-#: urls.py:34 urls.py:46
-msgid "comments/"
-msgstr "comentarios/"
-
-#: urls.py:35 urls.py:40 urls.py:54 templates/user_info.html:34
-msgid "edit/"
-msgstr "editar/"
-
-#: urls.py:36 urls.py:45
-msgid "revisions/"
-msgstr "revisiones/"
-
-#: urls.py:37 urls.py:38 urls.py:39 urls.py:40 urls.py:41 urls.py:42
-#: urls.py:43 urls.py:44 urls.py:45 urls.py:46 urls.py:47 forum/feed.py:19
-#: forum/models.py:306 forum/views.py:1416
-msgid "questions/"
-msgstr "preguntas/"
-
-#: urls.py:38 urls.py:64
-msgid "ask/"
-msgstr "preguntar/"
+#: django_authopenid/forms.py:70
+msgid "choose a username"
+msgstr ""
-#: urls.py:39
-msgid "unanswered/"
-msgstr "sinrespuesta/"
+#: django_authopenid/forms.py:76
+msgid "user name is required"
+msgstr ""
-#: urls.py:41
-msgid "close/"
-msgstr "cerrar/"
+#: django_authopenid/forms.py:77
+msgid "sorry, this name is taken, please choose another"
+msgstr ""
-#: urls.py:42
-msgid "reopen/"
-msgstr "reabrir/"
+#: django_authopenid/forms.py:78
+msgid "sorry, this name is not allowed, please choose another"
+msgstr ""
-#: urls.py:43
-msgid "answer/"
-msgstr "respuesta/"
+#: django_authopenid/forms.py:79
+msgid "sorry, there is no user with this name"
+msgstr ""
-#: urls.py:44
-msgid "vote/"
-msgstr "votar/"
+#: django_authopenid/forms.py:80
+msgid "sorry, we have a serious error - user name is taken by several users"
+msgstr ""
-#: urls.py:47 urls.py:48 django_authopenid/urls.py:29
-msgid "delete/"
-msgstr "borrar/"
+#: django_authopenid/forms.py:81
+msgid "user name can only consist of letters, empty space and underscore"
+msgstr ""
-#: urls.py:50
-msgid "question/"
-msgstr "pregunta/"
+#: django_authopenid/forms.py:116
+msgid "your email address"
+msgstr ""
-#: urls.py:51 urls.py:52 forum/views.py:740 forum/views.py:2013
-msgid "tags/"
-msgstr "etiquetas/"
+#: django_authopenid/forms.py:117
+msgid "email address is required"
+msgstr ""
-#: urls.py:53 urls.py:54 urls.py:55 forum/views.py:993 forum/views.py:997
-#: forum/views.py:1418 forum/views.py:1751 forum/views.py:2015
-msgid "users/"
-msgstr "usuarios/"
+#: django_authopenid/forms.py:118
+msgid "please enter a valid email address"
+msgstr ""
-#: urls.py:56 urls.py:57
-msgid "badges/"
-msgstr "distinciones/"
+#: django_authopenid/forms.py:119
+msgid "this email is already used by someone else, please choose another"
+msgstr ""
-#: urls.py:58
-msgid "messages/"
-msgstr "mensajes/"
+#: django_authopenid/forms.py:163 django_authopenid/views.py:118
+msgid "i-names are not supported"
+msgstr ""
-#: urls.py:58
-msgid "markread/"
-msgstr "marcarleido/"
+#: django_authopenid/forms.py:219
+msgid "Account with this name already exists on the forum"
+msgstr ""
-#: urls.py:60
-msgid "nimda/"
-msgstr "administrador/"
+#: django_authopenid/forms.py:220
+msgid "can't have two logins to the same account yet, sorry."
+msgstr ""
-#: urls.py:62
-msgid "upload/"
-msgstr "subir/"
+#: django_authopenid/forms.py:242
+msgid "Please enter valid username and password (both are case-sensitive)."
+msgstr ""
-#: urls.py:63 urls.py:64 urls.py:65
-msgid "books/"
-msgstr "libros/"
+#: django_authopenid/forms.py:245 django_authopenid/forms.py:295
+msgid "This account is inactive."
+msgstr ""
-#: urls.py:66
-msgid "search/"
-msgstr "buscar/"
+#: django_authopenid/forms.py:247
+msgid "Login failed."
+msgstr ""
-#: django_authopenid/forms.py:67 django_authopenid/views.py:102
-msgid "i-names are not supported"
-msgstr "i-names no son soportados"
+#: django_authopenid/forms.py:249
+msgid "Please enter username and password"
+msgstr ""
-#: django_authopenid/forms.py:102
-msgid ""
-"Usernames can only contain letters, numbers and "
-"underscores"
+#: django_authopenid/forms.py:251
+msgid "Please enter your password"
msgstr ""
-"Los nombres de usuario solo pueden contener letras, números y guión bajo"
-#: django_authopenid/forms.py:109
-msgid ""
-"This username does not exist in our database. Please "
-"choose another."
+#: django_authopenid/forms.py:253
+msgid "Please enter user name"
msgstr ""
-"Este nombre de usuario no existe en nuestra base de datos. Por favor elija "
-"otro."
-#: django_authopenid/forms.py:126 django_authopenid/forms.py:233
+#: django_authopenid/forms.py:291
msgid ""
"Please enter a valid username and password. Note that "
"both fields are case-sensitive."
msgstr ""
-"Por favor ingrese un usuario y contraseña validos. Ambos campos son "
-"sensibles a mayúsculas y minúsculas."
-#: django_authopenid/forms.py:130 django_authopenid/forms.py:237
-msgid "This account is inactive."
-msgstr "Esta cuenta esta inactiva."
-
-#: django_authopenid/forms.py:158 django_authopenid/forms.py:210
-msgid "invalid user name"
-msgstr "nombre de usuario no valido"
-
-#: django_authopenid/forms.py:160
-msgid "sorry, this name can not be used, please try another"
+#: django_authopenid/forms.py:313
+msgid "choose password"
msgstr ""
-"perdón, pero este nombre de usuario no puede ser usado, intente con otro"
-#: django_authopenid/forms.py:162
-msgid "username too short"
-msgstr "nombre de usuario muy corto"
-
-#: django_authopenid/forms.py:170 django_authopenid/forms.py:171
-msgid "this name is already in use - please try anoter"
-msgstr "este nombre ya está tomado - por favor intente con otro"
+#: django_authopenid/forms.py:314
+msgid "password is required"
+msgstr ""
-#: django_authopenid/forms.py:185
-msgid ""
-"This email is already registered in our database. "
-"Please choose another."
+#: django_authopenid/forms.py:317
+msgid "retype password"
msgstr ""
-"Este email ya está registrado en nuestra base de datos. Por favor, intente "
-"con otro."
-#: django_authopenid/forms.py:216
-msgid ""
-"This username don't exist. Please choose another."
-msgstr "Este nombre de usuario no existe, por favor ingrese otro."
+#: django_authopenid/forms.py:318
+msgid "please, retype your password"
+msgstr ""
-#: django_authopenid/forms.py:255
-msgid "choose a username"
-msgstr "elija un nombre de usuario"
+#: django_authopenid/forms.py:319
+msgid "sorry, entered passwords did not match, please try again"
+msgstr ""
-#: django_authopenid/forms.py:257 templates/authopenid/signup.html:38
-msgid "your email address"
-msgstr "su email (correo electrónico)"
+#: django_authopenid/forms.py:344
+msgid "Current password"
+msgstr ""
-#: django_authopenid/forms.py:259 templates/authopenid/signup.html:39
-msgid "choose password"
-msgstr "elija una contraseña"
+#: django_authopenid/forms.py:346
+msgid "New password"
+msgstr ""
-#: django_authopenid/forms.py:261 templates/authopenid/signup.html:40
-msgid "retype password"
-msgstr "re-ingrese la contraseña"
+#: django_authopenid/forms.py:348
+msgid "Retype new password"
+msgstr ""
-#: django_authopenid/forms.py:335
+#: django_authopenid/forms.py:359
msgid ""
"Old password is incorrect. Please enter the correct "
"password."
-msgstr "La antigua contraseña es incorrecta. Por favor ingrese la correcta"
+msgstr ""
-#: django_authopenid/forms.py:347
+#: django_authopenid/forms.py:371
msgid "new passwords do not match"
-msgstr "la nueva contraseña no coincide"
+msgstr ""
-#: django_authopenid/forms.py:442
+#: django_authopenid/forms.py:435
+msgid "Your user name (<i>required</i>)"
+msgstr ""
+
+#: django_authopenid/forms.py:450
msgid "Incorrect username."
-msgstr "Nombre de usuario incorrecto"
+msgstr ""
-#: django_authopenid/urls.py:10 forum/views.py:304 forum/views.py:699
+#: django_authopenid/urls.py:9 django_authopenid/urls.py:10
+#: django_authopenid/urls.py:11 django_authopenid/urls.py:13 forum/urls.py:29
+msgid "signin/"
+msgstr ""
+
+#: django_authopenid/urls.py:10
msgid "newquestion/"
-msgstr "nuevapregunta/"
+msgstr ""
#: django_authopenid/urls.py:11
msgid "newanswer/"
-msgstr "respuesta-nueva/"
+msgstr ""
#: django_authopenid/urls.py:12
msgid "signout/"
-msgstr "salir/"
+msgstr ""
#: django_authopenid/urls.py:13
msgid "complete/"
-msgstr "completado/"
+msgstr ""
#: django_authopenid/urls.py:15
-msgid "register/"
-msgstr "registrarse/"
+msgid "external-login/"
+msgstr ""
#: django_authopenid/urls.py:16
+msgid "register/"
+msgstr ""
+
+#: django_authopenid/urls.py:17
msgid "signup/"
-msgstr "registrarse/"
+msgstr ""
-#: django_authopenid/urls.py:18
+#: django_authopenid/urls.py:19
msgid "sendpw/"
-msgstr "enviarcontrasena/"
+msgstr ""
-#: django_authopenid/urls.py:27
+#: django_authopenid/urls.py:20 django_authopenid/urls.py:24
+msgid "password/"
+msgstr ""
+
+#: django_authopenid/urls.py:20
+msgid "confirm/"
+msgstr ""
+
+#: django_authopenid/urls.py:23
+msgid "account_settings"
+msgstr ""
+
+#: django_authopenid/urls.py:25 django_authopenid/urls.py:26
+#: django_authopenid/urls.py:27 django_authopenid/urls.py:28
+msgid "email/"
+msgstr ""
+
+#: django_authopenid/urls.py:25
msgid "validate/"
msgstr ""
-#: django_authopenid/views.py:108
+#: django_authopenid/urls.py:26
+msgid "change/"
+msgstr ""
+
+#: django_authopenid/urls.py:27
+msgid "sendkey/"
+msgstr ""
+
+#: django_authopenid/urls.py:28
+msgid "verify/"
+msgstr ""
+
+#: django_authopenid/urls.py:29
+msgid "openid/"
+msgstr ""
+
+#: django_authopenid/urls.py:30 forum/urls.py:49 forum/urls.py:53
+msgid "delete/"
+msgstr ""
+
+#: django_authopenid/views.py:124
#, python-format
msgid "OpenID %(openid_url)s is invalid"
-msgstr "El OpenID %(openid_url)s no es valido"
+msgstr ""
-#: django_authopenid/views.py:418 django_authopenid/views.py:545
-msgid "Welcome"
-msgstr "Bienvenido"
+#: django_authopenid/views.py:532
+msgid "Welcome email subject line"
+msgstr ""
-#: django_authopenid/views.py:508
+#: django_authopenid/views.py:627
msgid "Password changed."
-msgstr "Contraseña modificada"
+msgstr ""
-#: django_authopenid/views.py:520 django_authopenid/views.py:525
-msgid "your email needs to be validated"
-msgstr "su correo electrónico necesita ser validado"
+#: django_authopenid/views.py:639 django_authopenid/views.py:645
+#, python-format
+msgid "your email needs to be validated see %(details_url)s"
+msgstr ""
+
+#: django_authopenid/views.py:666
+msgid "Email verification subject line"
+msgstr ""
+
+#: django_authopenid/views.py:752
+msgid "your email was not changed"
+msgstr ""
-#: django_authopenid/views.py:682 django_authopenid/views.py:834
+#: django_authopenid/views.py:799 django_authopenid/views.py:951
#, python-format
msgid "No OpenID %s found associated in our database"
-msgstr "El OpenID %s no esta asociada en nuestra base de datos"
+msgstr ""
-#: django_authopenid/views.py:686 django_authopenid/views.py:841
+#: django_authopenid/views.py:803 django_authopenid/views.py:958
#, python-format
msgid "The OpenID %s isn't associated to current user logged in"
-msgstr "El OpenID %s no esta asociada al usuario actualmente autenticado"
+msgstr ""
-#: django_authopenid/views.py:694
+#: django_authopenid/views.py:811
msgid "Email Changed."
-msgstr "Email modificado"
+msgstr ""
-#: django_authopenid/views.py:769
+#: django_authopenid/views.py:886
msgid "This OpenID is already associated with another account."
-msgstr "Este OpenID ya está asociada a otra cuenta."
+msgstr ""
-#: django_authopenid/views.py:774
+#: django_authopenid/views.py:891
#, python-format
msgid "OpenID %s is now associated with your account."
-msgstr "El OpenID %s está ahora asociada con tu cuenta."
+msgstr ""
-#: django_authopenid/views.py:844
+#: django_authopenid/views.py:961
msgid "Account deleted."
-msgstr "Cuenta borrada."
+msgstr ""
-#: django_authopenid/views.py:884
+#: django_authopenid/views.py:1004
msgid "Request for new password"
-msgstr "Pedir nueva contraseña"
+msgstr ""
-#: django_authopenid/views.py:897
-msgid "A new password has been sent to your email address."
-msgstr "Una nueva contraseña ha sido enviada a tu cuenta de Email."
+#: django_authopenid/views.py:1017
+msgid "A new password and the activation link were sent to your email address."
+msgstr ""
-#: django_authopenid/views.py:927
+#: django_authopenid/views.py:1047
#, python-format
msgid ""
"Could not change password. Confirmation key '%s' is not "
"registered."
msgstr ""
-"No se ha podido modificar la contraseña. La clave de confirmación '%s' no "
-"está registrada"
-#: django_authopenid/views.py:936
+#: django_authopenid/views.py:1056
msgid ""
"Can not change password. User don't exist anymore in our "
"database."
msgstr ""
-"No se puede cambiar la contraseña. El usuario no existe más en nuestra base "
-"de datos."
-#: django_authopenid/views.py:945
+#: django_authopenid/views.py:1065
#, python-format
msgid "Password changed for %s. You may now sign in."
-msgstr "Contraseña cambiada por %s. Ahora puedes ingresar."
+msgstr ""
+
+#: forum/auth.py:484
+msgid "Your question and all of it's answers have been deleted"
+msgstr ""
+
+#: forum/auth.py:486
+msgid "Your question has been deleted"
+msgstr ""
+
+#: forum/auth.py:489
+msgid "The question and all of it's answers have been deleted"
+msgstr ""
+
+#: forum/auth.py:491
+msgid "The question has been deleted"
+msgstr ""
#: forum/const.py:8
msgid "duplicate question"
-msgstr "pregunta duplicada"
+msgstr ""
#: forum/const.py:9
-msgid "question if off-topic or not relevant"
-msgstr "pregunta esta fuera de tema o no es relevante"
+msgid "question is off-topic or not relevant"
+msgstr ""
#: forum/const.py:10
msgid "too subjective and argumentative"
-msgstr "demasiado subjetiva o argumentativa"
+msgstr ""
#: forum/const.py:11
msgid "is not an answer to the question"
-msgstr "no es una respuesta a la pregunta"
+msgstr ""
#: forum/const.py:12
msgid "the question is answered, right answer was accepted"
-msgstr "la pregunta esta respondida, se ha aceptado la respuesta correcta"
+msgstr ""
#: forum/const.py:13
msgid "problem is not reproducible or outdated"
-msgstr "el problema no es reproducible o caducó"
+msgstr ""
#: forum/const.py:15
msgid "question contains offensive inappropriate, or malicious remarks"
-msgstr "la pregunta contiene frases ofensivas, inapropiadas o maliciosas."
+msgstr ""
#: forum/const.py:16
msgid "spam or advertising"
-msgstr "spam o publicidad"
+msgstr ""
-#: forum/const.py:56
+#: forum/const.py:57
msgid "question"
-msgstr "pregunta"
+msgstr ""
-#: forum/const.py:57 templates/book.html:110
+#: forum/const.py:58 templates/book.html:110
msgid "answer"
-msgstr "respuesta"
+msgstr ""
-#: forum/const.py:58
+#: forum/const.py:59
msgid "commented question"
-msgstr "pregunta comentada"
+msgstr ""
-#: forum/const.py:59
+#: forum/const.py:60
msgid "commented answer"
-msgstr "respuesta comentada"
+msgstr ""
-#: forum/const.py:60
+#: forum/const.py:61
msgid "edited question"
-msgstr "pregunta editada"
+msgstr ""
-#: forum/const.py:61
+#: forum/const.py:62
msgid "edited answer"
-msgstr "respuesta editada"
+msgstr ""
-#: forum/const.py:62
+#: forum/const.py:63
msgid "received award"
-msgstr "premio recibido"
+msgstr ""
-#: forum/const.py:63
+#: forum/const.py:64
msgid "marked best answer"
-msgstr "marcada como mejor respuesta"
+msgstr ""
-#: forum/const.py:64
+#: forum/const.py:65
msgid "upvoted"
-msgstr "votada positivo"
+msgstr ""
-#: forum/const.py:65
+#: forum/const.py:66
msgid "downvoted"
-msgstr "votada negativo"
+msgstr ""
-#: forum/const.py:66
+#: forum/const.py:67
msgid "canceled vote"
-msgstr "voto cancelado"
+msgstr ""
-#: forum/const.py:67
+#: forum/const.py:68
msgid "deleted question"
-msgstr "pregunta borrada"
+msgstr ""
-#: forum/const.py:68
+#: forum/const.py:69
msgid "deleted answer"
-msgstr "respuesta borrada"
+msgstr ""
-#: forum/const.py:69
+#: forum/const.py:70
msgid "marked offensive"
-msgstr "marcada como ofensiva"
+msgstr ""
-#: forum/const.py:70
+#: forum/const.py:71
msgid "updated tags"
-msgstr "etiquetas actualizadas"
+msgstr ""
-#: forum/const.py:71
+#: forum/const.py:72
msgid "selected favorite"
-msgstr "seleccionada como favorita"
+msgstr ""
-#: forum/const.py:72
+#: forum/const.py:73
msgid "completed user profile"
-msgstr "completó perfil de usuario"
+msgstr ""
-#: forum/const.py:83
+#: forum/const.py:74
+msgid "email update sent to user"
+msgstr ""
+
+#: forum/const.py:85
msgid "[closed]"
-msgstr "[cerrada]"
+msgstr ""
-#: forum/const.py:84
+#: forum/const.py:86
msgid "[deleted]"
-msgstr "[borrada]"
+msgstr ""
-#: forum/const.py:85
+#: forum/const.py:87 forum/views.py:777 forum/views.py:796
msgid "initial version"
-msgstr "versión inicial"
+msgstr ""
-#: forum/const.py:86
+#: forum/const.py:88
msgid "retagged"
-msgstr "re-etiquetada"
+msgstr ""
+
+#: forum/const.py:92
+msgid "exclude ignored tags"
+msgstr ""
+
+#: forum/const.py:92
+msgid "allow only selected tags"
+msgstr ""
#: forum/feed.py:18
msgid " - "
-msgstr " - "
+msgstr ""
#: forum/feed.py:18
msgid "latest questions"
-msgstr "últimas preguntas"
+msgstr ""
-#: forum/forms.py:14 templates/answer_edit_tips.html:33
-#: templates/answer_edit_tips.html.py:37 templates/question_edit_tips.html:31
-#: templates/question_edit_tips.html:36
+#: forum/feed.py:19 forum/urls.py:57
+msgid "question/"
+msgstr ""
+
+#: forum/forms.py:16 templates/answer_edit_tips.html:35
+#: templates/answer_edit_tips.html.py:39 templates/question_edit_tips.html:32
+#: templates/question_edit_tips.html:37
msgid "title"
-msgstr "título"
+msgstr ""
-#: forum/forms.py:15
+#: forum/forms.py:17
msgid "please enter a descriptive title for your question"
-msgstr "ingrese un título descriptivo para su pregunta"
+msgstr ""
-#: forum/forms.py:20
+#: forum/forms.py:22
msgid "title must be > 10 characters"
-msgstr "el título debe tener al menos 10 caracteres"
+msgstr ""
-#: forum/forms.py:29
+#: forum/forms.py:31
msgid "content"
-msgstr "contenido"
+msgstr ""
-#: forum/forms.py:35
+#: forum/forms.py:37
msgid "question content must be > 10 characters"
-msgstr "el contenido de la pregunta debe ser al menos de 10 caracteres"
+msgstr ""
-#: forum/forms.py:45 templates/header.html:30 templates/header.html.py:64
+#: forum/forms.py:47 templates/header.html:28 templates/header.html.py:62
msgid "tags"
-msgstr "etiquetas"
+msgstr ""
-#: forum/forms.py:47
+#: forum/forms.py:49
msgid ""
"Tags are short keywords, with no spaces within. Up to five tags can be used."
msgstr ""
-"por favor utilice espacio para separar las etiquetas (esto habilitael auto-"
-"completado)"
-#: forum/forms.py:54 templates/question_retag.html:38
+#: forum/forms.py:56 templates/question_retag.html:39
msgid "tags are required"
-msgstr "las etiquetas son requeridas"
+msgstr ""
-#: forum/forms.py:58
+#: forum/forms.py:62
msgid "please use 5 tags or less"
-msgstr "por favor use 5 o menos etiquetas"
+msgstr ""
-#: forum/forms.py:61
+#: forum/forms.py:65
msgid "tags must be shorter than 20 characters"
-msgstr "las etiquetas deben ser menores a 20 caracteres"
+msgstr ""
-#: forum/forms.py:65
+#: forum/forms.py:69
msgid ""
"please use following characters in tags: letters 'a-z', numbers, and "
"characters '.-_#'"
msgstr ""
-"por favor use solo los siguientes caracteres en los nombres de etiquetas: "
-"letras 'a-z', números y caracteres '.-_#'"
-#: forum/forms.py:75 templates/index.html:57 templates/question.html:209
-#: templates/question.html.py:395 templates/questions.html:58
-#: templates/questions.html.py:70 templates/unanswered.html:48
-#: templates/unanswered.html.py:60
+#: forum/forms.py:79 templates/index.html:62 templates/index.html.py:74
+#: templates/post_contributor_info.html:7
+#: templates/question_summary_list_roll.html:26
+#: templates/question_summary_list_roll.html:38 templates/questions.html:96
+#: templates/questions.html.py:108 templates/unanswered.html:51
+#: templates/unanswered.html.py:63
msgid "community wiki"
-msgstr "wiki de comunidad"
+msgstr ""
-#: forum/forms.py:76
+#: forum/forms.py:80
msgid ""
"if you choose community wiki option, the question and answer do not generate "
"points and name of author will not be shown"
msgstr ""
-"si marca la opción 'wiki de comunidad', la pregunta y respuestas no generan "
-"puntos y el nombre del autor no será mostrado"
-#: forum/forms.py:89
+#: forum/forms.py:96
msgid "update summary:"
-msgstr "resumen de modificación"
+msgstr ""
-#: forum/forms.py:90
+#: forum/forms.py:97
msgid ""
"enter a brief summary of your revision (e.g. fixed spelling, grammar, "
"improved style, this field is optional)"
msgstr ""
-"ingresa un breve resumen de tu revisión (ej. error ortográfico, gramática, "
-"mejoras de estilo. Este campo es opcional."
-#: forum/forms.py:175
+#: forum/forms.py:100
+msgid "Automatically accept user's contributions for the email updates"
+msgstr ""
+
+#: forum/forms.py:113
+msgid "Your name:"
+msgstr ""
+
+#: forum/forms.py:114
+msgid "Email (not shared with anyone):"
+msgstr ""
+
+#: forum/forms.py:115
+msgid "Your message:"
+msgstr ""
+
+#: forum/forms.py:198
msgid "this email does not have to be linked to gravatar"
-msgstr "este email no tiene porque estar asociado a un Gravatar"
+msgstr ""
-#: forum/forms.py:176
+#: forum/forms.py:199
+msgid "Screen name"
+msgstr ""
+
+#: forum/forms.py:200
msgid "Real name"
-msgstr "Nombre real"
+msgstr ""
-#: forum/forms.py:177
+#: forum/forms.py:201
msgid "Website"
-msgstr "Sitio Web"
+msgstr ""
-#: forum/forms.py:178
+#: forum/forms.py:202
msgid "Location"
-msgstr "Ubicación"
+msgstr ""
-#: forum/forms.py:179
+#: forum/forms.py:203
msgid "Date of birth"
-msgstr "Fecha de nacimiento"
+msgstr ""
-#: forum/forms.py:179
+#: forum/forms.py:203
msgid "will not be shown, used to calculate age, format: YYYY-MM-DD"
-msgstr "no será mostrado, usado para calcular la edad. Formato: YYY-MM-DD"
+msgstr ""
-#: forum/forms.py:180 templates/authopenid/settings.html:21
+#: forum/forms.py:204 templates/authopenid/settings.html:21
msgid "Profile"
-msgstr "Perfil"
+msgstr ""
-#: forum/forms.py:207 forum/forms.py:208
+#: forum/forms.py:232 forum/forms.py:233
msgid "this email has already been registered, please use another one"
-msgstr "este email ya ha sido registrado, por favor use otro"
+msgstr ""
+
+#: forum/forms.py:239
+msgid "Choose email tag filter"
+msgstr ""
+
+#: forum/forms.py:254 forum/forms.py:255
+msgid "weekly"
+msgstr ""
+
+#: forum/forms.py:254 forum/forms.py:255
+msgid "no email"
+msgstr ""
+
+#: forum/forms.py:255
+msgid "daily"
+msgstr ""
+
+#: forum/forms.py:270
+msgid "Asked by me"
+msgstr ""
+
+#: forum/forms.py:273
+msgid "Answered by me"
+msgstr ""
+
+#: forum/forms.py:276
+msgid "Individually selected"
+msgstr ""
+
+#: forum/forms.py:279
+msgid "Entire forum (tag filtered)"
+msgstr ""
+
+#: forum/models.py:52
+msgid "Entire forum"
+msgstr ""
+
+#: forum/models.py:53
+msgid "Questions that I asked"
+msgstr ""
+
+#: forum/models.py:54
+msgid "Questions that I answered"
+msgstr ""
-#: forum/models.py:246
+#: forum/models.py:55
+msgid "Individually selected questions"
+msgstr ""
+
+#: forum/models.py:58
+msgid "Weekly"
+msgstr ""
+
+#: forum/models.py:59
+msgid "Daily"
+msgstr ""
+
+#: forum/models.py:60
+msgid "No email"
+msgstr ""
+
+#: forum/models.py:321
+#, python-format
msgid "%(author)s modified the question"
-msgstr "%(author)s modificó la pregunta"
+msgstr ""
-#: forum/models.py:250
+#: forum/models.py:325
#, python-format
msgid "%(people)s posted %(new_answer_count)s new answers"
-msgstr "%(people)s publicaron %(new_answer_count)s nuevas respuestas"
+msgstr ""
-#: forum/models.py:255
+#: forum/models.py:330
#, python-format
msgid "%(people)s commented the question"
-msgstr "%(people)s comentarion la pregunta"
+msgstr ""
-#: forum/models.py:260
+#: forum/models.py:335
#, python-format
msgid "%(people)s commented answers"
-msgstr "%(people)s comentaron la respuesta"
+msgstr ""
-#: forum/models.py:262
+#: forum/models.py:337
#, python-format
msgid "%(people)s commented an answer"
-msgstr "%(people)s comentaron la respuesta"
+msgstr ""
-#: forum/models.py:306 forum/models.py:418
-msgid "revisions"
-msgstr "revisiones/"
+#: forum/models.py:368
+msgid "interesting"
+msgstr ""
+
+#: forum/models.py:368
+msgid "ignored"
+msgstr ""
-#: forum/models.py:441 templates/badges.html:51
+#: forum/models.py:538 templates/badges.html:53
msgid "gold"
-msgstr "oro"
+msgstr ""
-#: forum/models.py:442 templates/badges.html:59
+#: forum/models.py:539 templates/badges.html:61
msgid "silver"
-msgstr "plata"
+msgstr ""
-#: forum/models.py:443 templates/badges.html:66
+#: forum/models.py:540 templates/badges.html:68
msgid "bronze"
-msgstr "bronce"
+msgstr ""
+
+#: forum/urls.py:26
+msgid "upfiles/"
+msgstr ""
+
+#: forum/urls.py:30
+msgid "about/"
+msgstr ""
+
+#: forum/urls.py:31
+msgid "faq/"
+msgstr ""
+
+#: forum/urls.py:32
+msgid "privacy/"
+msgstr ""
+
+#: forum/urls.py:33
+msgid "logout/"
+msgstr ""
+
+#: forum/urls.py:34 forum/urls.py:35 forum/urls.py:36 forum/urls.py:53
+msgid "answers/"
+msgstr ""
+
+#: forum/urls.py:34 forum/urls.py:46 forum/urls.py:49 forum/urls.py:53
+msgid "comments/"
+msgstr ""
+
+#: forum/urls.py:35 forum/urls.py:40 forum/urls.py:75
+#: templates/user_info.html:45
+msgid "edit/"
+msgstr ""
+
+#: forum/urls.py:36 forum/urls.py:45
+msgid "revisions/"
+msgstr ""
+
+#: forum/urls.py:37 forum/urls.py:38 forum/urls.py:39 forum/urls.py:40
+#: forum/urls.py:41 forum/urls.py:42 forum/urls.py:43 forum/urls.py:44
+#: forum/urls.py:45 forum/urls.py:46 forum/urls.py:49
+msgid "questions/"
+msgstr ""
+
+#: forum/urls.py:38 forum/urls.py:85
+msgid "ask/"
+msgstr ""
+
+#: forum/urls.py:39
+msgid "unanswered/"
+msgstr ""
+
+#: forum/urls.py:41
+msgid "close/"
+msgstr ""
+
+#: forum/urls.py:42
+msgid "reopen/"
+msgstr ""
+
+#: forum/urls.py:43
+msgid "answer/"
+msgstr ""
+
+#: forum/urls.py:44
+msgid "vote/"
+msgstr ""
+
+#: forum/urls.py:47
+msgid "command/"
+msgstr ""
+
+#: forum/urls.py:58 forum/urls.py:59
+msgid "tags/"
+msgstr ""
+
+#: forum/urls.py:61 forum/urls.py:65
+msgid "mark-tag/"
+msgstr ""
+
+#: forum/urls.py:61
+msgid "interesting/"
+msgstr ""
+
+#: forum/urls.py:65
+msgid "ignored/"
+msgstr ""
+
+#: forum/urls.py:69
+msgid "unmark-tag/"
+msgstr ""
+
+#: forum/urls.py:73 forum/urls.py:75 forum/urls.py:76
+msgid "users/"
+msgstr ""
+
+#: forum/urls.py:74
+msgid "moderate-user/"
+msgstr ""
+
+#: forum/urls.py:77 forum/urls.py:78
+msgid "badges/"
+msgstr ""
+
+#: forum/urls.py:79
+msgid "messages/"
+msgstr ""
+
+#: forum/urls.py:79
+msgid "markread/"
+msgstr ""
+
+#: forum/urls.py:81
+msgid "nimda/"
+msgstr ""
+
+#: forum/urls.py:83
+msgid "upload/"
+msgstr ""
+
+#: forum/urls.py:84 forum/urls.py:85 forum/urls.py:86
+msgid "books/"
+msgstr ""
+
+#: forum/urls.py:87
+msgid "search/"
+msgstr ""
+
+#: forum/urls.py:88
+msgid "feedback/"
+msgstr ""
+
+#: forum/urls.py:89
+msgid "account/"
+msgstr ""
#: forum/user.py:16 templates/user_tabs.html:7
msgid "overview"
-msgstr "vista general"
+msgstr ""
#: forum/user.py:17
msgid "user profile"
-msgstr "perfil de usuario"
+msgstr ""
#: forum/user.py:18
msgid "user profile overview"
-msgstr "vista general del perfil de usuario"
+msgstr ""
#: forum/user.py:24 templates/user_tabs.html:9
msgid "recent activity"
-msgstr "actividades recientes"
+msgstr ""
#: forum/user.py:25
msgid "recent user activity"
-msgstr "actividades recientes del usuario"
+msgstr ""
#: forum/user.py:26
msgid "profile - recent activity"
-msgstr "perfil - actividades recientes"
+msgstr ""
#: forum/user.py:33 templates/user_tabs.html:13
msgid "responses"
-msgstr "respuestas"
+msgstr ""
#: forum/user.py:34 templates/user_tabs.html:12
msgid "comments and answers to others questions"
-msgstr "comentarios y respuestas a preguntas de otros"
+msgstr ""
#: forum/user.py:35
msgid "profile - responses"
-msgstr "perfil - respuestas"
+msgstr ""
-#: forum/user.py:42 templates/user_info.html:23 templates/users.html:26
+#: forum/user.py:42 templates/users.html:26
msgid "reputation"
-msgstr "reputación"
+msgstr ""
#: forum/user.py:43
msgid "user reputation in the community"
-msgstr "reputación del usuario en la comunidad"
+msgstr ""
#: forum/user.py:44
msgid "profile - user reputation"
-msgstr "perfil - reputación del usuario"
+msgstr ""
#: forum/user.py:50
msgid "favorite questions"
-msgstr "preguntas favoritas"
+msgstr ""
#: forum/user.py:51
msgid "users favorite questions"
-msgstr "preguntas favoritas de los usuarios"
+msgstr ""
#: forum/user.py:52
msgid "profile - favorite questions"
-msgstr "perfil - preguntas favoritas"
+msgstr ""
#: forum/user.py:59 templates/user_tabs.html:20
msgid "casted votes"
-msgstr "votos"
+msgstr ""
#: forum/user.py:60 templates/user_tabs.html:20
msgid "user vote record"
-msgstr "historial de votación"
+msgstr ""
#: forum/user.py:61
msgid "profile - votes"
-msgstr "perfil - votos"
+msgstr ""
-#: forum/user.py:68
-msgid "preferences"
-msgstr "preferencias"
+#: forum/user.py:68 templates/user_tabs.html:28
+msgid "email subscriptions"
+msgstr ""
#: forum/user.py:69 templates/user_tabs.html:27
-msgid "user preference settings"
-msgstr "preferencias del usuario"
+msgid "email subscription settings"
+msgstr ""
#: forum/user.py:70
-msgid "profile - user preferences"
-msgstr "perfil - preferencia de "
+msgid "profile - email subscriptions"
+msgstr ""
+
+#: forum/views.py:141
+msgid "Q&A forum feedback"
+msgstr ""
+
+#: forum/views.py:142
+msgid "Thanks for the feedback!"
+msgstr ""
-#: forum/views.py:947
+#: forum/views.py:150
+msgid "We look forward to hearing your feedback! Please, give it next time :)"
+msgstr ""
+
+#: forum/views.py:1080
#, python-format
-msgid "subscription saved, %(email)s needs validation"
-msgstr "subscripción guardada, %(email)s necesita validación"
+msgid "subscription saved, %(email)s needs validation, see %(details_url)s"
+msgstr ""
+
+#: forum/views.py:1088
+msgid "email update frequency has been set to daily"
+msgstr ""
+
+#: forum/views.py:1965 forum/views.py:1969
+msgid "changes saved"
+msgstr ""
+
+#: forum/views.py:1975
+msgid "email updates canceled"
+msgstr ""
-#: forum/views.py:1860
+#: forum/views.py:2142
msgid "uploading images is limited to users with >60 reputation points"
-msgstr "para subir imagenes debes tener más de 60 puntos de reputación"
+msgstr ""
-#: forum/views.py:1862
+#: forum/views.py:2144
msgid "allowed file types are 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff'"
msgstr ""
-"los tipos de archivos permitidos son 'jpg', 'jpeg', 'gif', 'bmp', 'png', "
-"'tiff'"
-#: forum/views.py:1864
+#: forum/views.py:2146
#, python-format
msgid "maximum upload file size is %sK"
-msgstr "tamaño máximo permitido es archivo %sK"
+msgstr ""
-#: forum/views.py:1866
+#: forum/views.py:2148
#, python-format
msgid ""
"Error uploading file. Please contact the site administrator. Thank you. %s"
msgstr ""
-"Error al subir el archivo. Por favor, contacte al administrador. Gracias. %s"
-#: forum/management/commands/send_email_alerts.py:35
-msgid "updates from website"
-msgstr "actualizaciones del sitio"
+#: forum/management/commands/send_email_alerts.py:233
+msgid "email update message subject"
+msgstr ""
+
+#: forum/management/commands/send_email_alerts.py:234
+#, 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"
+msgstr[0] ""
+msgstr[1] ""
+
+#: forum/management/commands/send_email_alerts.py:243
+#: forum/management/commands/send_email_alerts.py:258
+msgid "new question"
+msgstr ""
+
+#: forum/management/commands/send_email_alerts.py:268
+#, python-format
+msgid "There is also one question which was recently "
+msgid_plural ""
+"There are also %(num)d more questions which were recently updated "
+msgstr[0] ""
+msgstr[1] ""
+
+#: forum/management/commands/send_email_alerts.py:273
+msgid ""
+"Perhaps you could look up previously sent forum reminders in your mailbox."
+msgstr ""
+
+#: forum/management/commands/send_email_alerts.py:278
+#, python-format
+msgid ""
+"go to %(link)s to change frequency of email updates or %(email)s "
+"administrator"
+msgstr ""
-#: forum/templatetags/extra_tags.py:143 forum/templatetags/extra_tags.py:172
-#: templates/header.html:35
+#: forum/templatetags/extra_tags.py:163 forum/templatetags/extra_tags.py:192
+#: templates/header.html:33
msgid "badges"
-msgstr "distinciones"
+msgstr ""
-#: forum/templatetags/extra_tags.py:144 forum/templatetags/extra_tags.py:171
+#: forum/templatetags/extra_tags.py:164 forum/templatetags/extra_tags.py:191
msgid "reputation points"
-msgstr "puntos de reputación"
+msgstr ""
+
+#: forum/templatetags/extra_tags.py:247
+msgid "%b %d at %H:%M"
+msgstr ""
+
+#: forum/templatetags/extra_tags.py:249
+msgid "%b %d '%y at %H:%M"
+msgstr ""
+
+#: forum/templatetags/extra_tags.py:251
+msgid "2 days ago"
+msgstr ""
+
+#: forum/templatetags/extra_tags.py:253
+msgid "yesterday"
+msgstr ""
+
+#: forum/templatetags/extra_tags.py:255
+#, python-format
+msgid "%(hr)d hour ago"
+msgid_plural "%(hr)d hours ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: forum/templatetags/extra_tags.py:257
+#, python-format
+msgid "%(min)d min ago"
+msgid_plural "%(min)d mins ago"
+msgstr[0] ""
+msgstr[1] ""
-#: forum/templatetags/extra_tags.py:225
-msgid " ago"
-msgstr " atras"
+#: middleware/anon_user.py:33
+#, python-format
+msgid "first time greeting with %(url)s"
+msgstr ""
#: templates/404.html:24
msgid "Sorry, could not find the page you requested."
-msgstr "Disculpe, no se pudo encontrar la página que solicito."
+msgstr ""
#: templates/404.html:26
msgid "This might have happened for the following reasons:"
-msgstr "Esto puede haber sucedido por alguno de los siguientes motivos:"
+msgstr ""
#: templates/404.html:28
msgid "this question or answer has been deleted;"
-msgstr "esta pregunta o respuesta ha sido borrada;"
+msgstr ""
#: templates/404.html:29
msgid "url has error - please check it;"
-msgstr "la url tiene un error - por favor compruebelo;"
+msgstr ""
#: templates/404.html:30
msgid ""
"the page you tried to visit is protected or you don't have sufficient "
"points, see"
msgstr ""
-"La pagina que intentas acceder esta protegida o no tienes los puntos de "
-"reputación suficientes, ver"
#: templates/404.html:31
msgid "if you believe this error 404 should not have occured, please"
-msgstr "si consideras que este error 404 no debería haber sucedido, por favor"
+msgstr ""
#: templates/404.html:32
msgid "report this problem"
-msgstr "reporta este problema"
+msgstr ""
#: templates/404.html:41 templates/500.html:27
msgid "back to previous page"
-msgstr "volver a la página siguiente"
+msgstr ""
#: templates/404.html:42
msgid "see all questions"
-msgstr "ver todas las preguntas"
+msgstr ""
#: templates/404.html:43
msgid "see all tags"
-msgstr "ver todas las tags"
+msgstr ""
#: templates/500.html:22
msgid "sorry, system error"
-msgstr "lo sentimos, ha habido un error del sistema"
+msgstr ""
#: templates/500.html:24
msgid "system error log is recorded, error will be fixed as soon as possible"
msgstr ""
-"el error del sistema ha sido registrado, y será solucionado lo antes postible"
#: templates/500.html:25
msgid "please report the error to the site administrators if you wish"
-msgstr "por favor reportar el error al administrador de ser posible"
+msgstr ""
#: templates/500.html:28
msgid "see latest questions"
-msgstr "ver ultimas preguntas"
+msgstr ""
#: templates/500.html:29
msgid "see tags"
-msgstr "ver tags"
+msgstr ""
#: templates/about.html:6 templates/about.html.py:11
msgid "About"
-msgstr "Acerca de"
+msgstr ""
-#: templates/answer_edit.html:4 templates/answer_edit.html.py:47
+#: templates/answer_edit.html:5 templates/answer_edit.html.py:48
msgid "Edit answer"
-msgstr "Editar respuesta"
+msgstr ""
-#: templates/answer_edit.html:24 templates/answer_edit.html.py:27
-#: templates/ask.html:25 templates/ask.html.py:28 templates/question.html:43
-#: templates/question.html.py:46 templates/question_edit.html:27
+#: templates/answer_edit.html:25 templates/answer_edit.html.py:28
+#: templates/ask.html:26 templates/ask.html.py:29 templates/question.html:45
+#: templates/question.html.py:48 templates/question_edit.html:25
+#: templates/question_edit.html.py:28
msgid "hide preview"
-msgstr "ocultar previsualización"
+msgstr ""
-#: templates/answer_edit.html:27 templates/ask.html:28
-#: templates/question.html:46 templates/question_edit.html:27
+#: templates/answer_edit.html:28 templates/ask.html:29
+#: templates/question.html:48 templates/question_edit.html:28
msgid "show preview"
-msgstr "ver previsualización"
+msgstr ""
-#: templates/answer_edit.html:47 templates/question_edit.html:65
-#: templates/question_retag.html:52 templates/revisions_answer.html:36
-#: templates/revisions_question.html:36
+#: templates/answer_edit.html:48 templates/question_edit.html:66
+#: templates/question_retag.html:53 templates/revisions_answer.html:38
+#: templates/revisions_question.html:38
msgid "back"
-msgstr "volver"
+msgstr ""
-#: templates/answer_edit.html:52 templates/question_edit.html:70
-#: templates/revisions_answer.html:47 templates/revisions_question.html:47
+#: templates/answer_edit.html:53 templates/question_edit.html:71
+#: templates/revisions_answer.html:52 templates/revisions_question.html:52
msgid "revision"
-msgstr "revisión"
+msgstr ""
-#: templates/answer_edit.html:55 templates/question_edit.html:74
+#: templates/answer_edit.html:56 templates/question_edit.html:75
msgid "select revision"
-msgstr "seleccionar revisión"
+msgstr ""
-#: templates/answer_edit.html:62 templates/ask.html:94
-#: templates/question.html:467 templates/question_edit.html:91
+#: templates/answer_edit.html:63 templates/ask.html:97
+#: templates/question.html:442 templates/question_edit.html:92
msgid "Toggle the real time Markdown editor preview"
-msgstr "Activar la visualización en tiempo real de Markdown"
+msgstr ""
-#: templates/answer_edit.html:62 templates/ask.html:94
-#: templates/question.html:467 templates/question_edit.html:91
+#: templates/answer_edit.html:63 templates/ask.html:97
+#: templates/question.html:443 templates/question_edit.html:92
msgid "toggle preview"
-msgstr "Activar previsualización"
+msgstr ""
-#: templates/answer_edit.html:71 templates/question_edit.html:115
-#: templates/question_retag.html:73
+#: templates/answer_edit.html:72 templates/question_edit.html:124
+#: templates/question_retag.html:74
msgid "Save edit"
-msgstr "Guardar la edición"
+msgstr ""
-#: templates/answer_edit.html:72 templates/close.html:29
-#: templates/question_edit.html:116 templates/question_retag.html:74
-#: templates/reopen.html:30 templates/user_edit.html:83
-#: templates/authopenid/changeemail.html:34
+#: templates/answer_edit.html:73 templates/close.html:29
+#: templates/feedback.html:50 templates/question_edit.html:125
+#: templates/question_retag.html:75 templates/reopen.html:30
+#: templates/user_edit.html:87 templates/authopenid/changeemail.html:40
msgid "Cancel"
-msgstr "Cancelar"
+msgstr ""
#: templates/answer_edit_tips.html:4
msgid "answer tips"
-msgstr "sugerencias sobre respuestas"
+msgstr ""
#: templates/answer_edit_tips.html:7
msgid "please make your answer relevant to this community"
-msgstr "por favor, haz que tu respuesta sea relevante a esta comunidad"
+msgstr ""
#: templates/answer_edit_tips.html:10
msgid "try to give an answer, rather than engage into a discussion"
-msgstr "intenta dar una respuesta, más que entablar un debate o discusión"
+msgstr ""
#: templates/answer_edit_tips.html:13
msgid "please try to provide details"
-msgstr "por favor, intenta brindar detalles"
+msgstr ""
#: templates/answer_edit_tips.html:16 templates/question_edit_tips.html:13
msgid "be clear and concise"
-msgstr "ser claro y conciso"
+msgstr ""
-#: templates/answer_edit_tips.html:19 templates/question_edit_tips.html:16
+#: templates/answer_edit_tips.html:20 templates/question_edit_tips.html:17
msgid "see frequently asked questions"
-msgstr "ver preguntas frecuentes"
+msgstr ""
-#: templates/answer_edit_tips.html:24 templates/question_edit_tips.html:22
+#: templates/answer_edit_tips.html:26 templates/question_edit_tips.html:23
msgid "Markdown tips"
-msgstr "sugerencias de Markdown"
+msgstr ""
-#: templates/answer_edit_tips.html:27 templates/question_edit_tips.html:25
+#: templates/answer_edit_tips.html:29 templates/question_edit_tips.html:26
msgid "*italic* or __italic__"
-msgstr "*itálica* o __itálica__"
+msgstr ""
-#: templates/answer_edit_tips.html:30 templates/question_edit_tips.html:28
+#: templates/answer_edit_tips.html:32 templates/question_edit_tips.html:29
msgid "**bold** or __bold__"
-msgstr "**negrita** o __negrita__"
+msgstr ""
-#: templates/answer_edit_tips.html:33 templates/question_edit_tips.html:31
+#: templates/answer_edit_tips.html:35 templates/question_edit_tips.html:32
msgid "link"
-msgstr "enlace"
+msgstr ""
-#: templates/answer_edit_tips.html:33 templates/answer_edit_tips.html.py:37
-#: templates/question_edit_tips.html:31 templates/question_edit_tips.html:36
+#: templates/answer_edit_tips.html:35 templates/answer_edit_tips.html.py:39
+#: templates/question_edit_tips.html:32 templates/question_edit_tips.html:37
msgid "text"
-msgstr "texto"
+msgstr ""
-#: templates/answer_edit_tips.html:37 templates/question_edit_tips.html:36
+#: templates/answer_edit_tips.html:39 templates/question_edit_tips.html:37
msgid "image"
-msgstr "imagen"
+msgstr ""
-#: templates/answer_edit_tips.html:41 templates/question_edit_tips.html:40
+#: templates/answer_edit_tips.html:43 templates/question_edit_tips.html:41
msgid "numbered list:"
-msgstr "lista numerada"
+msgstr ""
-#: templates/answer_edit_tips.html:46 templates/question_edit_tips.html:45
+#: templates/answer_edit_tips.html:48 templates/question_edit_tips.html:46
msgid "basic HTML tags are also supported"
-msgstr "etiquetas básicas de HTML permitidas"
+msgstr ""
-#: templates/answer_edit_tips.html:49 templates/question_edit_tips.html:48
+#: templates/answer_edit_tips.html:52 templates/question_edit_tips.html:50
msgid "learn more about Markdown"
-msgstr "aprender mas sobre Markdown"
+msgstr ""
-#: templates/ask.html:4 templates/ask.html.py:60
+#: templates/ask.html:5 templates/ask.html.py:61
msgid "Ask a question"
-msgstr "Hacer una pregunta"
+msgstr ""
-#: templates/ask.html:67
+#: templates/ask.html:68
msgid "login to post question info"
msgstr ""
-"<span class='strong big'>Puedes comenzar a realizar tu pregunta de forma "
-"anonima</span> - actualmente no te encuentras Ingresado. Cuando envíes tu "
-"pregunta, serás redireccionado a la página de Ingreso/registro. Tu pregunta "
-"será guardada temporalemente y será enviada cuando ingreses. El proceso de "
-"Ingreso/Registro es muy simple. Ingresar lleva unos 30 segundos, registrarse "
-"por primera vez lleva un minuto."
-#: templates/ask.html:73
+#: templates/ask.html:74
#, python-format
-msgid "must have valid %(email)s to post"
+msgid ""
+"must have valid %(email)s to post, \n"
+" see %(email_validation_faq_url)s\n"
+" "
msgstr ""
-"<span class='strong big'>Parece ser que tu dirección de email, %(email)s no "
-"ha sido validada aún.</span> Para enviar mensajes debes verificar tu "
-"dirección de email, puedes ver <a href='/faq#validate'>más detalles aquí</"
-"a>. <br/> Puedes enviar tu pregunta ahora y validar tu email luego. Tu "
-"pregunta será guardada mientras tanto y publicada cuando valides tu email."
-#: templates/ask.html:107
+#: templates/ask.html:112 templates/ask.html.py:119
+#: templates/question_edit.html:120
msgid "(required)"
-msgstr "(requerido)"
+msgstr ""
-#: templates/ask.html:114
+#: templates/ask.html:126
msgid "Login/signup to post your question"
-msgstr "Iniciar sesión/registrarse para publicar su pregunta"
+msgstr ""
-#: templates/ask.html:116
+#: templates/ask.html:128
msgid "Ask your question"
-msgstr "Haz tu pregunta"
+msgstr ""
#: templates/badge.html:6 templates/badge.html.py:17
msgid "Badge"
-msgstr "Distinción"
+msgstr ""
#: templates/badge.html:26
msgid "The users have been awarded with badges:"
-msgstr "Usuarios han sido galardonados con distinciones:"
+msgstr ""
#: templates/badges.html:6
msgid "Badges summary"
-msgstr "Resumen de distinciones"
+msgstr ""
-#: templates/badges.html:17 templates/user_stats.html:115
+#: templates/badges.html:17
msgid "Badges"
-msgstr "Distinciones"
+msgstr ""
#: templates/badges.html:21
msgid "Community gives you awards for your questions, answers and votes."
-msgstr "La comunidad te da distinciones por tus preguntas, respuestas y votos."
+msgstr ""
#: templates/badges.html:22
+#, python-format
msgid ""
-"Below is the list of available badges and number of times each type of badge "
-"has been awarded."
+"Below is the list of available badges and number \n"
+" of times each type of badge has been awarded. Give us feedback at %"
+"(feedback_faq_url)s.\n"
+" "
msgstr ""
-"Debajo esta la lista de las distinciones disponibles y la cantidad de veces "
-"que han sido asignadas."
-#: templates/badges.html:48
+#: templates/badges.html:50
msgid "Community badges"
-msgstr "Distinciones de la comunidad"
+msgstr ""
-#: templates/badges.html:54
+#: templates/badges.html:56
msgid "gold badge description"
msgstr ""
-"Las distinciones de Oro son excepcionales. Para obtenerla debes demostrar un "
-"profundo conocimiento y habilidad además de participar activamente en la "
-"comunidad. La distinción de Oro es la condecoración máxima en esta comunidad"
-#: templates/badges.html:62
+#: templates/badges.html:64
msgid "silver badge description"
msgstr ""
-"Obtener una distinción de Plata requiere de paciencia. Si has logrado una, "
-"quiere decir que haz significativamente aportado a esta comunidad."
-#: templates/badges.html:65
+#: templates/badges.html:67
msgid "bronze badge: often given as a special honor"
msgstr ""
-"distinción de bronce: con frecuencia entregada como reconocimiento especial."
-#: templates/badges.html:69
+#: templates/badges.html:71
msgid "bronze badge description"
msgstr ""
-"Si eres un usuario activo de esta comunidad, recibirás esta distinción - de "
-"todas maneras es un honor especial."
#: templates/book.html:7
msgid "reading channel"
-msgstr "canal de lectura"
+msgstr ""
#: templates/book.html:26
msgid "[author]"
-msgstr "[autor]"
+msgstr ""
#: templates/book.html:30
msgid "[publisher]"
-msgstr "[editorial]"
+msgstr ""
#: templates/book.html:34
msgid "[publication date]"
-msgstr "[fecha de publicación]"
+msgstr ""
#: templates/book.html:38
msgid "[price]"
-msgstr "[precio]"
+msgstr ""
#: templates/book.html:39
msgid "currency unit"
-msgstr "unidad de moneda"
+msgstr ""
#: templates/book.html:42
msgid "[pages]"
-msgstr "[páginas]"
+msgstr ""
#: templates/book.html:43
msgid "pages abbreviation"
-msgstr "abreviación de páginas"
+msgstr ""
#: templates/book.html:46
msgid "[tags]"
-msgstr "[etiquetas]"
+msgstr ""
#: templates/book.html:56
msgid "author blog"
-msgstr "blog del autor"
+msgstr ""
#: templates/book.html:62
msgid "book directory"
-msgstr "directorio del libro"
+msgstr ""
#: templates/book.html:66
msgid "buy online"
-msgstr "comprar en-linea"
+msgstr ""
#: templates/book.html:79
msgid "reader questions"
-msgstr "pregunta de lector"
+msgstr ""
#: templates/book.html:82
msgid "ask the author"
-msgstr "preguntar al autor"
+msgstr ""
#: templates/book.html:88 templates/book.html.py:93
-#: templates/users_questions.html:17
+#: templates/users_questions.html:18
msgid "this question was selected as favorite"
-msgstr "esta pregunta ha sido seleccionada como favorita"
+msgstr ""
#: templates/book.html:88 templates/book.html.py:93
-#: templates/users_questions.html:11 templates/users_questions.html.py:17
+#: templates/users_questions.html:11 templates/users_questions.html.py:18
msgid "number of times"
-msgstr "numero de veces"
+msgstr ""
-#: templates/book.html:105 templates/index.html:48 templates/questions.html:46
-#: templates/unanswered.html:37 templates/users_questions.html:30
+#: templates/book.html:105 templates/index.html:50
+#: templates/question_summary_list_roll.html:14 templates/questions.html:84
+#: templates/unanswered.html:39 templates/users_questions.html:32
msgid "votes"
-msgstr "votos"
+msgstr ""
#: templates/book.html:108
msgid "the answer has been accepted to be correct"
-msgstr "la respuesta ha sido aceptada como correcta"
+msgstr ""
-#: templates/book.html:115 templates/index.html:49 templates/questions.html:47
-#: templates/unanswered.html:38 templates/users_questions.html:40
+#: templates/book.html:115 templates/index.html:51
+#: templates/question_summary_list_roll.html:15 templates/questions.html:85
+#: templates/unanswered.html:40 templates/users_questions.html:40
msgid "views"
-msgstr "vistas"
+msgstr ""
-#: templates/book.html:125 templates/index.html:69 templates/question.html:499
-#: templates/questions.html:84 templates/questions.html.py:156
-#: templates/tags.html:49 templates/unanswered.html:75
-#: templates/unanswered.html.py:106 templates/users_questions.html:52
+#: templates/book.html:125 templates/index.html:106
+#: templates/question.html:488 templates/question_summary_list_roll.html:52
+#: templates/questions.html:140 templates/questions.html.py:257
+#: templates/tags.html:49 templates/unanswered.html:95
+#: templates/unanswered.html.py:122 templates/users_questions.html:52
msgid "using tags"
-msgstr "usando etiquetas"
+msgstr ""
#: templates/book.html:147
msgid "subscribe to book RSS feed"
-msgstr "suscribirse al RSS del libro"
+msgstr ""
-#: templates/book.html:147 templates/index.html:118
+#: templates/book.html:147 templates/index.html:157
msgid "subscribe to the questions feed"
-msgstr "suscribirse al agregado de noticias"
+msgstr ""
#: templates/close.html:6 templates/close.html.py:16
msgid "Close question"
-msgstr "Cerrar pregunta"
+msgstr ""
#: templates/close.html:19
msgid "Close the question"
-msgstr "Cerrar la pregunta"
+msgstr ""
#: templates/close.html:25
msgid "Reasons"
-msgstr "Razón"
+msgstr ""
#: templates/close.html:28
msgid "OK to close"
-msgstr "OK para cerrar"
+msgstr ""
#: templates/faq.html:11
msgid "Frequently Asked Questions "
-msgstr "Preguntas Frecuentes"
+msgstr ""
#: templates/faq.html:16
msgid "What kinds of questions can I ask here?"
-msgstr "¿Qué clase de preguntas puedo hacer aquí?"
+msgstr ""
#: templates/faq.html:17
msgid ""
"Most importanly - questions should be <strong>relevant</strong> to this "
"community."
msgstr ""
-"Por encima de todo - las preguntas deben ser <strong>relevantes</strong>a "
-"esta comunidad."
#: templates/faq.html:18
msgid ""
"Before asking the question - please make sure to use search to see whether "
"your question has alredy been answered."
msgstr ""
-"Antes de hacer tu pregunta - por favor usa el buscador para asegurarte que "
-"la pregunta no este ya hecha."
#: templates/faq.html:21
msgid "What questions should I avoid asking?"
-msgstr "¿Qué preguntas debería evitar preguntar?"
+msgstr ""
#: templates/faq.html:22
msgid ""
"Please avoid asking questions that are not relevant to this community, too "
"subjective and argumentative."
msgstr ""
-"Evita hacer preguntas que no son relevantes a la comunidad, demasiado "
-"subjetivas o argumentativas."
#: templates/faq.html:27
msgid "What should I avoid in my answers?"
-msgstr "¿Que debo evitar en mis respuestas?"
+msgstr ""
#: templates/faq.html:28
msgid ""
@@ -1181,42 +1455,32 @@ msgid ""
"discussions in your answers, comment facility allows some space for brief "
"discussions."
msgstr ""
-"es un sitio de Preguntas y Respuestas, no un grupo de discusión. Por ende, "
-"intenta evitar discusiones en tus respuestas. Los comentarios permiten "
-"realizar pequeñas discusiones."
#: templates/faq.html:32
msgid "Who moderates this community?"
-msgstr "¿Quién modera esta comunidad?"
+msgstr ""
#: templates/faq.html:33
msgid "The short answer is: <strong>you</strong>."
-msgstr "La respuesta corta es: <strong>tú</strong>"
+msgstr ""
#: templates/faq.html:34
msgid "This website is moderated by the users."
-msgstr "Este sitio es moderado por los usuarios."
+msgstr ""
#: templates/faq.html:35
msgid ""
"The reputation system allows users earn the authorization to perform a "
"variety of moderation tasks."
msgstr ""
-"El sistema de reputación permite a los usuarios adquirir autorización para "
-"realizar diversas tareas de moderación."
#: templates/faq.html:40
msgid "How does reputation system work?"
-msgstr "¿Cómo funciona el sistema de reputación?"
+msgstr ""
#: templates/faq.html:41
msgid "Rep system summary"
msgstr ""
-"Cuando una pregunta o respuesta es votada positivamente, el usuario que la "
-"realizo ganará algunos puntos, que llamamos \"puntos de reputación\". Estos "
-"puntos sirven a groso modo para medir la confianza que la comunidad le "
-"tiene. Diversas tareas de moderación son gradualmente asignadas a los "
-"usuarios basado en estos puntos de reputación."
#: templates/faq.html:42
msgid ""
@@ -1228,657 +1492,804 @@ msgid ""
"or answer. The table below explains reputation point requirements for each "
"type of moderation task."
msgstr ""
-"Por ejemplo, si haces una pregunta interesante o das una respuesta útil, tu "
-"adición será votada positivamente. Por otro lado, si la respuesta es fuera "
-"de lugar - será votada negativamente. Cada voto a favor genera <strong>10</"
-"strong> puntos, cada voto en contra restará <strong>2</strong> puntos. Hay "
-"un limite de <strong>200</strong> puntos que puedes acumular por pregunta o "
-"respuesta. La tabla debajo explica los puntos de reputación requeridos para "
-"cada tarea de moderación."
-#: templates/faq.html:53 templates/user_votes.html:14
+#: templates/faq.html:53 templates/user_votes.html:15
msgid "upvote"
-msgstr "votar positivo"
+msgstr ""
#: templates/faq.html:57
msgid "use tags"
-msgstr "etiquetas usadas"
+msgstr ""
#: templates/faq.html:62
msgid "add comments"
-msgstr "agregar comentarios"
+msgstr ""
-#: templates/faq.html:66 templates/user_votes.html:16
+#: templates/faq.html:66 templates/user_votes.html:17
msgid "downvote"
-msgstr "votar negativo"
+msgstr ""
#: templates/faq.html:69
msgid "open and close own questions"
-msgstr "abrir y cerrar sus propias preguntas"
+msgstr ""
#: templates/faq.html:73
msgid "retag questions"
-msgstr "re-etiquetar preguntas"
+msgstr ""
-#: templates/faq.html:77
+#: templates/faq.html:78
msgid "edit community wiki questions"
-msgstr "editar preguntas de la wiki comunitaria"
+msgstr ""
-#: templates/faq.html:81
+#: templates/faq.html:83
msgid "edit any answer"
-msgstr "editar cualquier pregunta"
+msgstr ""
-#: templates/faq.html:85
+#: templates/faq.html:87
msgid "open any closed question"
-msgstr "abrir cualquier pregunta cerrada"
+msgstr ""
-#: templates/faq.html:89
+#: templates/faq.html:91
msgid "delete any comment"
-msgstr "borrar cualquier comentario"
+msgstr ""
-#: templates/faq.html:93
+#: templates/faq.html:95
msgid "delete any questions and answers and perform other moderation tasks"
msgstr ""
-"borrar cualquier pregunta o respuesta y realizar otras tareas de "
-"administración."
-#: templates/faq.html:100
+#: templates/faq.html:102
msgid "how to validate email title"
-msgstr "¿Cómo validar mi correo electrónico?"
+msgstr ""
-#: templates/faq.html:102
-msgid "how to validate email info"
-msgstr ""
-"<form style='margin:0;padding:0;' action='/email/sendkey/'><p><span class="
-"\"bigger strong\">¿Cómo?</span> Si acabas de asignar o cambiar tu correo "
-"electrónico - <strong>verifica tu casilla de mensajes y clickea en el link "
-"incluido</strong>. <br/> El link contiene una clave generada especificamente "
-"para ti. <button style='display:inline' type='submit'><strong>get a new key</"
-"strong></button> y vuelve a revisar tu casilla de mensajes.</p></form><span "
-"class=\"bigger strong\">¿Porqué?</span> La validación del email es requerida "
-"para estar seguros the que <strong>solo tu puedes enviar mensajes</strong> "
-"bajo tu concentimiento y para <strong>minimizar el spam</strong>.<br/> Con "
-"tu email podrás <strong>suscribirte a actualizaciones</strong> en las "
-"preguntas mas interesantes. También, cuando te registras por primera vez - "
-"se crea un imagen personal única de <a href='/"
-"faq#gravatar'><strong>gravatar</strong></a>."
-
-#: templates/faq.html:106
+#: templates/faq.html:104
+#, python-format
+msgid ""
+"how to validate email info with %(send_email_key_url)s %(gravatar_faq_url)s"
+msgstr ""
+
+#: templates/faq.html:108
msgid "what is gravatar"
-msgstr "¿Qué es gravatar?"
+msgstr ""
-#: templates/faq.html:107
+#: templates/faq.html:109
msgid "gravatar faq info"
msgstr ""
-"<strong>Gravatar</strong> significa <strong>g</strong>lobalmente <strong>r</"
-"strong>econocido <strong>avatar</strong> - tu imagen única asociada a tu "
-"email. Es simplemente una imagen que se muestra junto con tus mensajes en "
-"sitios que soportan gravatar. Por defecto gravatar aparece como un cuadrado "
-"rellenado con figuras parecidas a copos de nieve. Puedes <strong>seleccionar "
-"tu imagen</strong> en <a href='http://gravatar.com'><strong>gravatar.com</"
-"strong></a>"
-#: templates/faq.html:110
+#: templates/faq.html:112
msgid "To register, do I need to create new password?"
-msgstr "¿Para registrarme, debo crearme una cuenta?"
+msgstr ""
-#: templates/faq.html:111
+#: templates/faq.html:113
msgid ""
"No, you don't have to. You can login through any service that supports "
"OpenID, e.g. Google, Yahoo, AOL, etc."
msgstr ""
-"No tienes porqué. Puedes ingresar usando cualquiera de los servicios que "
-"soportan OpenID, ej. Google, Yahoo, AOL, MyOpenID, etc."
-#: templates/faq.html:112
+#: templates/faq.html:114
msgid "Login now!"
-msgstr "Ingresa ahora!"
+msgstr ""
-#: templates/faq.html:117
+#: templates/faq.html:119
msgid "Why other people can edit my questions/answers?"
-msgstr "¿Porqué otras personas pueden editar mis preguntas y respuestas?"
+msgstr ""
-#: templates/faq.html:118
+#: templates/faq.html:120
msgid "Goal of this site is..."
msgstr ""
-"El objetivo de este sitio es generar contenido valioso mediante preguntas y "
-"respuestas, de forma colaborativa. "
-#: templates/faq.html:118
+#: templates/faq.html:120
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 ""
-"Entonces, las preguntas y respuestas pueden ser editadas como wiki por "
-"usuarios con experiencia, y esto mejora la calidad general del conocimiento "
-"acumulado."
-#: templates/faq.html:119
+#: templates/faq.html:121
msgid "If this approach is not for you, we respect your choice."
msgstr ""
-"Si esta forma de funcionamiento no es de tu agrado, respetamos tu elección."
-#: templates/faq.html:123
+#: templates/faq.html:125
msgid "Still have questions?"
-msgstr "¿Aún tienes preguntas?"
+msgstr ""
-#: templates/faq.html:124
-msgid "Please ask your question, help make our community better!"
-msgstr "Por favor haz tu pregunta, ¡ayudanos a mejorar nuestra comunidad!"
+#: templates/faq.html:126
+#, python-format
+msgid ""
+"Please ask your question at %(ask_question_url)s, help make our community "
+"better!"
+msgstr ""
-#: templates/faq.html:126 templates/header.html:29 templates/header.html.py:63
+#: templates/faq.html:128 templates/header.html:27 templates/header.html.py:61
msgid "questions"
-msgstr "preguntas"
+msgstr ""
-#: templates/faq.html:126 templates/index.html:123
+#: templates/faq.html:128 templates/index.html:162
msgid "."
-msgstr "."
+msgstr ""
+
+#: templates/feedback.html:6
+msgid "Feedback"
+msgstr ""
+
+#: templates/feedback.html:11
+msgid "Give us your feedback!"
+msgstr ""
+
+#: templates/feedback.html:17
+#, python-format
+msgid ""
+"\n"
+" <span class='big strong'>Dear %(user_name)s</span>, we look "
+"forward to hearing your feedback. \n"
+" Please type and send us your message below.\n"
+" "
+msgstr ""
+
+#: templates/feedback.html:24
+msgid ""
+"\n"
+" <span class='big strong'>Dear visitor</span>, we look forward to "
+"hearing your feedback.\n"
+" Please type and send us your message below.\n"
+" "
+msgstr ""
-#: templates/footer.html:7 templates/header.html:14 templates/index.html:83
+#: templates/feedback.html:41
+msgid "(this field is required)"
+msgstr ""
+
+#: templates/feedback.html:49
+msgid "Send Feedback"
+msgstr ""
+
+#: templates/footer.html:8 templates/header.html:13 templates/index.html:120
msgid "about"
-msgstr "acerca de nosotros"
+msgstr ""
-#: templates/footer.html:8 templates/header.html:15 templates/index.html:84
-#: templates/question_edit_tips.html:16
+#: templates/footer.html:9 templates/header.html:14 templates/index.html:121
+#: templates/question_edit_tips.html:17
msgid "faq"
-msgstr "preguntas frecuentes"
+msgstr ""
-#: templates/footer.html:9
+#: templates/footer.html:10
msgid "blog"
-msgstr "blog"
+msgstr ""
-#: templates/footer.html:10
+#: templates/footer.html:11
msgid "contact us"
-msgstr "contactenos"
+msgstr ""
-#: templates/footer.html:11
+#: templates/footer.html:12
msgid "privacy policy"
-msgstr "código de privacidad"
+msgstr ""
-#: templates/footer.html:12
+#: templates/footer.html:21
msgid "give feedback"
-msgstr "envía comentarios"
-
-#: templates/footer.html:18
-msgid "current revision"
-msgstr "revisión actual"
+msgstr ""
-#: templates/header.html:10
+#: templates/header.html:9
msgid "logout"
-msgstr "salir"
+msgstr ""
-#: templates/header.html:12 templates/authopenid/signup.html:41
+#: templates/header.html:11
msgid "login"
-msgstr "entrar"
+msgstr ""
-#: templates/header.html:23
+#: templates/header.html:21
msgid "back to home page"
-msgstr "volver página principal"
+msgstr ""
-#: templates/header.html:31 templates/header.html.py:65
+#: templates/header.html:29 templates/header.html.py:63
msgid "users"
-msgstr "usuarios"
+msgstr ""
-#: templates/header.html:33
+#: templates/header.html:31
msgid "books"
-msgstr "libros"
+msgstr ""
-#: templates/header.html:36
+#: templates/header.html:34
msgid "unanswered questions"
-msgstr "sin respuesta"
+msgstr ""
-#: templates/header.html:40
+#: templates/header.html:38
msgid "my profile"
-msgstr "mi perfil"
+msgstr ""
-#: templates/header.html:44
+#: templates/header.html:42
msgid "ask a question"
-msgstr "hacer una pregunta"
+msgstr ""
-#: templates/header.html:59
+#: templates/header.html:57
msgid "search"
-msgstr "buscar"
+msgstr ""
-#: templates/index.html:7
+#: templates/index.html:8
msgid "Home"
-msgstr "Inicio"
+msgstr ""
-#: templates/index.html:22 templates/questions.html:7
+#: templates/index.html:25 templates/questions.html:8
msgid "Questions"
-msgstr "Preguntas"
+msgstr ""
-#: templates/index.html:24
+#: templates/index.html:27
msgid "last updated questions"
-msgstr "ultimas preguntas actualizadas"
+msgstr ""
-#: templates/index.html:24 templates/questions.html:25
-#: templates/unanswered.html:20
+#: templates/index.html:27 templates/questions.html:51
+#: templates/unanswered.html:21
msgid "newest"
-msgstr "más nuevas"
+msgstr ""
+
+#: templates/index.html:28 templates/questions.html:52
+msgid "most recently updated questions"
+msgstr ""
-#: templates/index.html:25 templates/questions.html:27
+#: templates/index.html:28 templates/questions.html:52
+msgid "active"
+msgstr ""
+
+#: templates/index.html:29 templates/questions.html:53
msgid "hottest questions"
-msgstr "preguntas calientes"
+msgstr ""
-#: templates/index.html:25 templates/questions.html:27
+#: templates/index.html:29 templates/questions.html:53
msgid "hottest"
-msgstr "más calientes"
+msgstr ""
-#: templates/index.html:26 templates/questions.html:28
+#: templates/index.html:30 templates/questions.html:54
msgid "most voted questions"
-msgstr "preguntas más votadas"
+msgstr ""
-#: templates/index.html:26 templates/questions.html:28
+#: templates/index.html:30 templates/questions.html:54
msgid "most voted"
-msgstr "más votadas"
+msgstr ""
-#: templates/index.html:27
+#: templates/index.html:31
msgid "all questions"
-msgstr "todas las preguntas"
+msgstr ""
-#: templates/index.html:47 templates/questions.html:45
-#: templates/unanswered.html:36 templates/users_questions.html:35
+#: templates/index.html:49 templates/question_summary_list_roll.html:13
+#: templates/questions.html:83 templates/unanswered.html:38
+#: templates/users_questions.html:36
msgid "answers"
-msgstr "respuestas"
+msgstr ""
+
+#: templates/index.html:81 templates/index.html.py:95
+#: templates/questions.html:115 templates/questions.html.py:129
+#: templates/unanswered.html:70 templates/unanswered.html.py:84
+msgid "Posted:"
+msgstr ""
+
+#: templates/index.html:84 templates/index.html.py:89
+#: templates/questions.html:118 templates/questions.html.py:123
+#: templates/unanswered.html:73 templates/unanswered.html.py:78
+msgid "Updated:"
+msgstr ""
-#: templates/index.html:69 templates/question.html:499
-#: templates/questions.html:84 templates/questions.html.py:156
-#: templates/tags.html:49 templates/unanswered.html:75
-#: templates/unanswered.html.py:106 templates/users_questions.html:52
+#: templates/index.html:106 templates/question.html:488
+#: templates/question_summary_list_roll.html:52 templates/questions.html:140
+#: templates/questions.html.py:257 templates/tags.html:49
+#: templates/unanswered.html:95 templates/unanswered.html.py:122
+#: templates/users_questions.html:52
msgid "see questions tagged"
-msgstr "ver preguntas etiquetadas"
+msgstr ""
-#: templates/index.html:80
+#: templates/index.html:117
msgid "welcome to website"
-msgstr "bienvenido a sitio"
+msgstr ""
-#: templates/index.html:89
+#: templates/index.html:128
msgid "Recent tags"
-msgstr "Etiquetas recientes"
+msgstr ""
-#: templates/index.html:94 templates/question.html:125
+#: templates/index.html:133 templates/question.html:135
#, python-format
msgid "see questions tagged '%(tagname)s'"
-msgstr "ver preguntas etiquetadas '%(tagname)s'"
+msgstr ""
-#: templates/index.html:97 templates/index.html.py:123
+#: templates/index.html:136 templates/index.html.py:162
msgid "popular tags"
-msgstr "etiquetas populares"
+msgstr ""
-#: templates/index.html:102
+#: templates/index.html:141
msgid "Recent awards"
-msgstr "Reconocimientos recientes"
+msgstr ""
-#: templates/index.html:108
+#: templates/index.html:147
msgid "given to"
-msgstr "dados a"
+msgstr ""
-#: templates/index.html:113
+#: templates/index.html:152
msgid "all awards"
-msgstr "todos los reconocimientos"
+msgstr ""
-#: templates/index.html:118
+#: templates/index.html:157
msgid "subscribe to last 30 questions by RSS"
-msgstr "suscribirse a las últimas 30 preguntas por RSS"
+msgstr ""
-#: templates/index.html:123
+#: templates/index.html:162
msgid "Still looking for more? See"
-msgstr "¿Aún sigues buscando más? Ver"
+msgstr ""
-#: templates/index.html:123
+#: templates/index.html:162
msgid "complete list of questions"
-msgstr "lista completa de preguntas"
+msgstr ""
-#: templates/index.html:123
+#: templates/index.html:162 templates/authopenid/signup.html:18
msgid "or"
-msgstr "ó"
+msgstr ""
-#: templates/index.html:123
+#: templates/index.html:162
msgid "Please help us answer"
-msgstr "Ayudanos a responder"
+msgstr ""
-#: templates/index.html:123
+#: templates/index.html:162
msgid "list of unanswered questions"
-msgstr "lista de preguntas sin respuesta"
+msgstr ""
-#: templates/logout.html:6 templates/logout.html.py:17
+#: templates/logout.html:6 templates/logout.html.py:16
msgid "Logout"
-msgstr "Salir"
+msgstr ""
-#: templates/logout.html:20
+#: templates/logout.html:19
msgid ""
"As a registered user you can login with your OpenID, log out of the site or "
"permanently remove your account."
msgstr ""
-"Como usuario registrado puedes ingresar con tu OpenID, salir del sitio o "
-"eliminar de forma permanente tu cuenta."
-#: templates/logout.html:21
+#: templates/logout.html:20
msgid "Logout now"
-msgstr "Salir ahora"
+msgstr ""
#: templates/pagesize.html:6
msgid "posts per page"
-msgstr "entradas por página"
+msgstr ""
#: templates/paginator.html:6 templates/paginator.html.py:7
msgid "previous"
-msgstr "previo"
+msgstr ""
#: templates/paginator.html:19
msgid "current page"
-msgstr "página actúal"
+msgstr ""
#: templates/paginator.html:22 templates/paginator.html.py:29
msgid "page number "
-msgstr "número de página"
+msgstr ""
#: templates/paginator.html:22 templates/paginator.html.py:29
msgid "number - make blank in english"
-msgstr " "
+msgstr ""
#: templates/paginator.html:33
msgid "next page"
-msgstr "próxima página"
+msgstr ""
+
+#: templates/post_contributor_info.html:9
+#, python-format
+msgid ""
+"\n"
+" one revision\n"
+" "
+msgid_plural ""
+"\n"
+" %(rev_count)s revisions\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/post_contributor_info.html:19
+msgid "asked"
+msgstr ""
+
+#: templates/post_contributor_info.html:22
+msgid "answered"
+msgstr ""
+
+#: templates/post_contributor_info.html:24
+msgid "posted"
+msgstr ""
+
+#: templates/post_contributor_info.html:45
+msgid "updated"
+msgstr ""
#: templates/privacy.html:6 templates/privacy.html.py:11
msgid "Privacy policy"
-msgstr "Privacidad"
+msgstr ""
#: templates/privacy.html:15
msgid "general message about privacy"
-msgstr "mensaje de privacidad"
+msgstr ""
#: templates/privacy.html:18
msgid "Site Visitors"
-msgstr "Visitantes del Sitio"
+msgstr ""
#: templates/privacy.html:20
msgid "what technical information is collected about visitors"
-msgstr "que información es recolectada sobre los usuarios"
+msgstr ""
#: templates/privacy.html:23
msgid "Personal Information"
-msgstr "Información Personal"
+msgstr ""
#: templates/privacy.html:25
msgid "details on personal information policies"
-msgstr "describir código de manejo de la información personal"
+msgstr ""
#: templates/privacy.html:28
msgid "Other Services"
-msgstr "Otros servicios"
+msgstr ""
#: templates/privacy.html:30
msgid "details on sharing data with third parties"
-msgstr "detalles sobre compartir información con terceros"
+msgstr ""
#: templates/privacy.html:35
msgid "cookie policy details"
-msgstr "uso de cookies"
+msgstr ""
#: templates/privacy.html:37
msgid "Policy Changes"
-msgstr "Cambios de Códigos"
+msgstr ""
#: templates/privacy.html:38
msgid "how privacy policies can be changed"
-msgstr "como pueden ser cambiados los códigos de privacidad"
+msgstr ""
-#: templates/question.html:72 templates/question.html.py:73
-#: templates/question.html:85 templates/question.html.py:87
+#: templates/question.html:77 templates/question.html.py:78
+#: templates/question.html:94 templates/question.html.py:96
msgid "i like this post (click again to cancel)"
-msgstr "Me gusta esta entrada (clickear devuelta para cancelar)"
+msgstr ""
-#: templates/question.html:75 templates/question.html.py:89
-#: templates/question.html:289
+#: templates/question.html:80 templates/question.html.py:98
+#: templates/question.html:257
msgid "current number of votes"
-msgstr "número actual de votos"
+msgstr ""
-#: templates/question.html:80 templates/question.html.py:81
-#: templates/question.html:94 templates/question.html.py:95
+#: templates/question.html:89 templates/question.html.py:90
+#: templates/question.html:103 templates/question.html.py:104
msgid "i dont like this post (click again to cancel)"
-msgstr "No me gusta esta entrada (clickear devuelta para cancelar)"
+msgstr ""
-#: templates/question.html:100 templates/question.html.py:101
+#: templates/question.html:109 templates/question.html.py:110
msgid "mark this question as favorite (click again to cancel)"
-msgstr "marcar esta pregunta como favorita (clickear devuelta para cancelar)"
+msgstr ""
-#: templates/question.html:107 templates/question.html.py:108
+#: templates/question.html:116 templates/question.html.py:117
msgid "remove favorite mark from this question (click again to restore mark)"
msgstr ""
-"remover marca de favorito a esta pregunta (clickear devuelta para volver a "
-"marcar)"
-#: templates/question.html:134 templates/question.html.py:322
-#: templates/revisions_answer.html:53 templates/revisions_question.html:53
+#: templates/question.html:140 templates/question.html.py:294
+#: templates/revisions_answer.html:58 templates/revisions_question.html:58
msgid "edit"
-msgstr "editar"
-
-#: templates/question.html:138 templates/question.html.py:332
-msgid "delete"
-msgstr "borrar"
+msgstr ""
-#: templates/question.html:143
+#: templates/question.html:145
msgid "reopen"
-msgstr "re-abrir"
+msgstr ""
-#: templates/question.html:148
+#: templates/question.html:149
msgid "close"
-msgstr "cerrar"
+msgstr ""
-#: templates/question.html:154 templates/question.html.py:345
+#: templates/question.html:155 templates/question.html.py:300
msgid ""
"report as offensive (i.e containing spam, advertising, malicious text, etc.)"
msgstr ""
-"reportar como ofensivo (ej. contiene spam, publicidad, texto malicioso, etc.)"
-#: templates/question.html:155 templates/question.html.py:346
+#: templates/question.html:156 templates/question.html.py:301
msgid "flag offensive"
-msgstr "marcar como ofensivo"
-
-#: templates/question.html:167 templates/question.html.py:355
-#: templates/revisions_answer.html:65 templates/revisions_question.html:65
-msgid "updated"
-msgstr "actualizado"
+msgstr ""
-#: templates/question.html:216 templates/question.html.py:402
-#: templates/revisions_answer.html:63 templates/revisions_question.html:63
-msgid "asked"
-msgstr "preguntado"
+#: templates/question.html:164 templates/question.html.py:312
+msgid "delete"
+msgstr ""
-#: templates/question.html:246 templates/question.html.py:429
-msgid "comments"
-msgstr "comentarios"
+#: templates/question.html:182 templates/question.html.py:332
+msgid "delete this comment"
+msgstr ""
-#: templates/question.html:247 templates/question.html.py:430
+#: templates/question.html:193 templates/question.html.py:343
+#: templates/question.html:367
msgid "add comment"
-msgstr "agregar comentario"
+msgstr ""
+
+#: templates/question.html:197
+#, python-format
+msgid ""
+"\n"
+" see <strong>one</strong> more \n"
+" "
+msgid_plural ""
+"\n"
+" see <strong>%(counter)s</strong> "
+"more\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/question.html:203
+#, python-format
+msgid ""
+"\n"
+" see <strong>one</strong> more "
+"comment\n"
+" "
+msgid_plural ""
+"\n"
+" see <strong>%(counter)s</strong> "
+"more comments\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
-#: templates/question.html:260
+#: templates/question.html:219
#, python-format
msgid ""
-"The question has been closed for the following reason \"%(question."
-"get_close_reason_display)s\" by"
+"The question has been closed for the following reason \"%(close_reason)s\" by"
msgstr ""
-"La pregunta ha sido cerrada por el siguiente motivo \"%(question."
-"get_close_reason_display)s\" por"
-#: templates/question.html:262
+#: templates/question.html:221
#, python-format
-msgid "close date %(question.closed_at)s"
-msgstr "fecha de cerrada %(question.closed_at)s"
+msgid "close date %(closed_at)s"
+msgstr ""
-#: templates/question.html:269 templates/user_stats.html:28
-msgid "Answers"
-msgstr "Respuestas"
+#: templates/question.html:229
+#, python-format
+msgid ""
+"\n"
+" One Answer:\n"
+" "
+msgid_plural ""
+"\n"
+" %(counter)s Answers:\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
-#: templates/question.html:271
+#: templates/question.html:237
msgid "oldest answers will be shown first"
-msgstr "la respuesta mas vieja será mostrada primero"
+msgstr ""
-#: templates/question.html:271
+#: templates/question.html:237
msgid "oldest answers"
-msgstr "pregunta más vieja"
+msgstr ""
-#: templates/question.html:272
+#: templates/question.html:239
msgid "newest answers will be shown first"
-msgstr "preguntas más nuevas serán mostradas primero"
+msgstr ""
-#: templates/question.html:272
+#: templates/question.html:239
msgid "newest answers"
-msgstr "más nuevas"
+msgstr ""
-#: templates/question.html:273
+#: templates/question.html:241
msgid "most voted answers will be shown first"
-msgstr "las preguntas más votadas serán mostradas primero"
+msgstr ""
-#: templates/question.html:273
+#: templates/question.html:241
msgid "popular answers"
-msgstr "respuestas populares"
+msgstr ""
-#: templates/question.html:287 templates/question.html.py:288
+#: templates/question.html:255 templates/question.html.py:256
msgid "i like this answer (click again to cancel)"
-msgstr "me gusta esta respuesta (clickear devuelta para cancelar)"
+msgstr ""
-#: templates/question.html:294 templates/question.html.py:295
+#: templates/question.html:262 templates/question.html.py:263
msgid "i dont like this answer (click again to cancel)"
-msgstr "no me gusta esta respuesta (clickear devuelta para cancelar)"
+msgstr ""
-#: templates/question.html:300 templates/question.html.py:301
+#: templates/question.html:268 templates/question.html.py:269
msgid "mark this answer as favorite (click again to undo)"
-msgstr "marcar esta respuesta como favorita (clickear devuelta para deshacer)"
+msgstr ""
-#: templates/question.html:306 templates/question.html.py:307
+#: templates/question.html:274 templates/question.html.py:275
msgid "the author of the question has selected this answer as correct"
-msgstr "el autor de esta pregunta ha seleccionado esta respuesta como correcta"
-
-#: templates/question.html:329
-msgid "undelete"
-msgstr "deshacer eliminar"
+msgstr ""
-#: templates/question.html:339
+#: templates/question.html:288
msgid "answer permanent link"
-msgstr "enlace permanente a respuesta"
+msgstr ""
-#: templates/question.html:340
+#: templates/question.html:289
msgid "permanent link"
-msgstr "enlace permanente"
+msgstr ""
+
+#: templates/question.html:312
+msgid "undelete"
+msgstr ""
+
+#: templates/question.html:347
+#, python-format
+msgid ""
+"\n"
+" see <strong>one</"
+"strong> more \n"
+" "
+msgid_plural ""
+"\n"
+" see <strong>%"
+"(counter)s</strong> more\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/question.html:353
+#, python-format
+msgid ""
+"\n"
+" see <strong>one</"
+"strong> more comment\n"
+" "
+msgid_plural ""
+"\n"
+" see <strong>%"
+"(counter)s</strong> more comments\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/question.html:366
+msgid "comments"
+msgstr ""
+
+#: templates/question.html:386 templates/question.html.py:389
+msgid "Notify me once a day when there are any new answers"
+msgstr ""
+
+#: templates/question.html:392
+msgid "Notify me weekly when there are any new answers"
+msgstr ""
+
+#: templates/question.html:397
+#, python-format
+msgid ""
+"\n"
+" You can always adjust frequency of email updates from your %"
+"(profile_url)s\n"
+" "
+msgstr ""
-#: templates/question.html:453
+#: templates/question.html:404
+msgid "once you sign in you will be able to subscribe for any updates here"
+msgstr ""
+
+#: templates/question.html:415
msgid "Your answer"
-msgstr "Tu respuesta"
+msgstr ""
-#: templates/question.html:456
+#: templates/question.html:417
+msgid "Be the first one to answer this question!"
+msgstr ""
+
+#: templates/question.html:423
msgid "you can answer anonymously and then login"
-msgstr "puedes responder de forma anónima y luego ingresar"
+msgstr ""
-#: templates/question.html:479
-msgid "Answer the question"
-msgstr "Responde la pregunta"
+#: templates/question.html:427
+msgid "answer your own question only to give an answer"
+msgstr ""
-#: templates/question.html:481
-msgid "Notify me daily if there are any new answers."
-msgstr "Notificarme diariamente si hay nuevas respuestas."
+#: templates/question.html:429
+msgid "please only give an answer, no discussions"
+msgstr ""
-#: templates/question.html:483
-msgid "once you sign in you will be able to subscribe for any updates here"
+#: templates/question.html:465
+msgid "Login/Signup to Post Your Answer"
+msgstr ""
+
+#: templates/question.html:468
+msgid "Answer Your Own Question"
msgstr ""
-"una vez que hayas ingresado podrás suscribirte a cualquiera de las "
-"actualizaciones aquí."
-#: templates/question.html:494
+#: templates/question.html:470
+msgid "Answer the question"
+msgstr ""
+
+#: templates/question.html:483
msgid "Question tags"
-msgstr "Tags de la pregunta"
+msgstr ""
-#: templates/question.html:504
+#: templates/question.html:493
msgid "question asked"
-msgstr "pregunta preguntada"
-
-#: templates/question.html:504 templates/question.html.py:510
-#: templates/user_info.html:51
-msgid "ago"
-msgstr " atras"
+msgstr ""
-#: templates/question.html:507
+#: templates/question.html:496
msgid "question was seen"
-msgstr "la pregunta fue vista"
+msgstr ""
-#: templates/question.html:507
+#: templates/question.html:496
msgid "times"
-msgstr "veces"
+msgstr ""
-#: templates/question.html:510
+#: templates/question.html:499
msgid "last updated"
-msgstr "última vez actualizada"
+msgstr ""
-#: templates/question.html:515
+#: templates/question.html:504
msgid "Related questions"
-msgstr "Preguntas relacionadas"
+msgstr ""
-#: templates/question_edit.html:4 templates/question_edit.html.py:65
+#: templates/question_edit.html:5 templates/question_edit.html.py:66
msgid "Edit question"
-msgstr "Editar pregunta"
+msgstr ""
#: templates/question_edit_tips.html:4
msgid "question tips"
-msgstr "sugerencias sobre pregunta"
+msgstr ""
#: templates/question_edit_tips.html:7
msgid "please ask a relevant question"
-msgstr "por favor hacer preguntas relevantes"
+msgstr ""
#: templates/question_edit_tips.html:10
msgid "please try provide enough details"
-msgstr "intente proveer suficientes detalles"
+msgstr ""
-#: templates/question_retag.html:3 templates/question_retag.html.py:52
+#: templates/question_retag.html:4 templates/question_retag.html.py:53
msgid "Change tags"
-msgstr "Cambiar etiquetas"
+msgstr ""
-#: templates/question_retag.html:39
+#: templates/question_retag.html:40
msgid "up to 5 tags, less than 20 characters each"
-msgstr "hasta 5 etiquetas, menos de 20 caracteres cada una"
+msgstr ""
-#: templates/question_retag.html:82
+#: templates/question_retag.html:83
msgid "Why use and modify tags?"
-msgstr "¿Porqué usar y modificar etiquetas?"
+msgstr ""
-#: templates/question_retag.html:85
+#: templates/question_retag.html:86
msgid "tags help us keep Questions organized"
-msgstr "las etiquetas nos permiten mantener las Preguntas organizadas"
+msgstr ""
-#: templates/question_retag.html:91
+#: templates/question_retag.html:94
msgid "tag editors receive special awards from the community"
msgstr ""
-"los editores de etiquetas reciben distinciones especiales de la comunidad"
-#: templates/questions.html:23
+#: templates/questions.html:28 templates/questions.html.py:32
msgid "Found by tags"
-msgstr "Encontradas por etiqueta"
+msgstr ""
-#: templates/questions.html:23
+#: templates/questions.html:28 templates/questions.html.py:38
msgid "Found by title"
-msgstr "Encontradas por título"
+msgstr ""
-#: templates/questions.html:23
+#: templates/questions.html:28 templates/questions.html.py:44
msgid "All questions"
-msgstr "Todas las preguntas"
+msgstr ""
+
+#: templates/questions.html:36
+msgid "Search results"
+msgstr ""
+
+#: templates/questions.html:42 templates/unanswered.html:8
+#: templates/unanswered.html.py:19
+msgid "Unanswered questions"
+msgstr ""
-#: templates/questions.html:25 templates/unanswered.html:20
+#: templates/questions.html:51 templates/unanswered.html:21
msgid "most recently asked questions"
-msgstr "preguntas hechas más recientemente"
+msgstr ""
-#: templates/questions.html:26
-msgid "most recently updated questions"
-msgstr "preguntas actualizadas más recientemente"
+#: templates/questions.html:143
+msgid "Category: "
+msgstr ""
-#: templates/questions.html:26
-msgid "active"
-msgstr "actividad"
+#: templates/questions.html:149
+msgid "Did not find anything?"
+msgstr ""
+
+#: templates/questions.html:152
+msgid "Did not find what you were looking for?"
+msgstr ""
+
+#: templates/questions.html:154
+msgid "Please, post your question!"
+msgstr ""
-#: templates/questions.html:109
+#: templates/questions.html:169
#, python-format
msgid ""
"\n"
@@ -1889,15 +2300,9 @@ msgid_plural ""
"\t\t\thave total %(q_num)s questions tagged %(tagname)s\n"
"\t\t\t"
msgstr[0] ""
-"\n"
-"\t\t\ttiene un total de %(q_num)s preguntas etiquetadas con %(tagname)s\n"
-"\t\t\t"
msgstr[1] ""
-"\n"
-"\t\t\ttiene un total de %(q_num)s preguntas etiquetadas con %(tagname)s\n"
-"\t\t\t"
-#: templates/questions.html:116
+#: templates/questions.html:176
#, python-format
msgid ""
"\n"
@@ -1908,16 +2313,10 @@ msgid_plural ""
"\t\t\t\thave total %(q_num)s questions containing %(searchtitle)s\n"
"\t\t\t\t"
msgstr[0] ""
-"\n"
-"\t\t\thay un total de %(q_num)s preguntas que contienen %(searchtitle)s\n"
-"\t\t\t"
msgstr[1] ""
-"\n"
-"\t\t\thay un total de %(q_num)s pregunta que contiene %(searchtitle)s\n"
-"\t\t\t"
-#: templates/questions.html:122
-#, fuzzy, python-format
+#: templates/questions.html:182
+#, python-format
msgid ""
"\n"
"\t\t\t\thave total %(q_num)s questions\n"
@@ -1926,628 +2325,750 @@ msgid_plural ""
"\n"
"\t\t\t\thave total %(q_num)s questions\n"
"\t\t\t\t"
-msgstr[0] "ver preguntas etiquetadas '%(tagname)s'"
-msgstr[1] "ver pregunta etiquetada '%(tagname)s'"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/questions.html:191
+#, python-format
+msgid ""
+"\n"
+" have total %(q_num)s questions tagged %(tagname)s\n"
+" "
+msgid_plural ""
+"\n"
+" have total %(q_num)s questions tagged %(tagname)s\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/questions.html:199
+#, python-format
+msgid ""
+"\n"
+" have total %(q_num)s questions containing %(searchtitle)"
+"s in full text\n"
+" "
+msgid_plural ""
+"\n"
+" have total %(q_num)s questions containing %(searchtitle)"
+"s in full text\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/questions.html:205
+#, python-format
+msgid ""
+"\n"
+" have total %(q_num)s questions containing %(searchtitle)"
+"s\n"
+" "
+msgid_plural ""
+"\n"
+" have total %(q_num)s questions containing %(searchtitle)"
+"s\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/questions.html:213
+#, python-format
+msgid ""
+"\n"
+" have total %(q_num)s unanswered questions\n"
+" "
+msgid_plural ""
+"\n"
+" have total %(q_num)s unanswered questions\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/questions.html:219
+#, python-format
+msgid ""
+"\n"
+" have total %(q_num)s questions\n"
+" "
+msgid_plural ""
+"\n"
+" have total %(q_num)s questions\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
-#: templates/questions.html:131
+#: templates/questions.html:230
msgid "latest questions info"
-msgstr "<strong>Más recientes</strong> preguntas son mostradas primero."
+msgstr ""
-#: templates/questions.html:135
+#: templates/questions.html:234
msgid "Questions are sorted by the <strong>time of last update</strong>."
msgstr ""
-"Las preguntas estan ordenadas por <strong>fecha de último update</strong>."
-#: templates/questions.html:136
+#: templates/questions.html:235
msgid "Most recently answered ones are shown first."
-msgstr "Las más recientemente respondidas son mostradas primero."
+msgstr ""
-#: templates/questions.html:140
+#: templates/questions.html:239
msgid "Questions sorted by <strong>number of responses</strong>."
-msgstr "Preguntas ordenadas por <strong>número de respuestas</strong>."
+msgstr ""
-#: templates/questions.html:141
+#: templates/questions.html:240
msgid "Most answered questions are shown first."
-msgstr "Preguntas más respondidas aparecen primero."
+msgstr ""
-#: templates/questions.html:145
+#: templates/questions.html:244
msgid "Questions are sorted by the <strong>number of votes</strong>."
-msgstr "Las preguntas son ordenadas por el <strong>número de votos</strong>."
+msgstr ""
-#: templates/questions.html:146
+#: templates/questions.html:245
msgid "Most voted questions are shown first."
-msgstr "Las preguntas más votadas son mostradas primero."
+msgstr ""
-#: templates/questions.html:153 templates/unanswered.html:102
+#: templates/questions.html:253 templates/unanswered.html:118
msgid "Related tags"
-msgstr "Etiquetas relacionadas"
+msgstr ""
+
+#: templates/questions.html:263 templates/tag_selector.html:10
+#: templates/tag_selector.html.py:27
+#, python-format
+msgid "see questions tagged '%(tag_name)s'"
+msgstr ""
#: templates/reopen.html:6 templates/reopen.html.py:16
msgid "Reopen question"
-msgstr "Re-abrir pregunta"
+msgstr ""
#: templates/reopen.html:19
msgid "Open the previously closed question"
-msgstr "Abrir pregunta previamente cerrada"
+msgstr ""
#: templates/reopen.html:22
msgid "The question was closed for the following reason "
-msgstr "La pregunta fue cerrada por el siguiente motivo "
+msgstr ""
#: templates/reopen.html:22
msgid "reason - leave blank in english"
-msgstr "razón - "
+msgstr ""
#: templates/reopen.html:22
msgid "on "
-msgstr "el "
+msgstr ""
#: templates/reopen.html:22
msgid "date closed"
-msgstr "fecha cerrada"
+msgstr ""
#: templates/reopen.html:29
msgid "Reopen this question"
-msgstr "Re-abrir esta pregunta"
+msgstr ""
-#: templates/revisions_answer.html:7 templates/revisions_answer.html.py:36
-#: templates/revisions_question.html:8 templates/revisions_question.html:36
+#: templates/revisions_answer.html:7 templates/revisions_answer.html.py:38
+#: templates/revisions_question.html:8 templates/revisions_question.html:38
msgid "Revision history"
-msgstr "Historial de revisiones"
+msgstr ""
+
+#: templates/revisions_answer.html:50 templates/revisions_question.html:50
+msgid "click to hide/show revision"
+msgstr ""
+
+#: templates/tag_selector.html:4
+msgid "Interesting tags"
+msgstr ""
+
+#: templates/tag_selector.html:14
+#, python-format
+msgid "remove '%(tag_name)s' from the list of interesting tags"
+msgstr ""
+
+#: templates/tag_selector.html:20 templates/tag_selector.html.py:37
+msgid "Add"
+msgstr ""
+
+#: templates/tag_selector.html:21
+msgid "Ignored tags"
+msgstr ""
+
+#: templates/tag_selector.html:31
+#, python-format
+msgid "remove '%(tag_name)s' from the list of ignored tags"
+msgstr ""
+
+#: templates/tag_selector.html:40
+msgid "keep ingored questions hidden"
+msgstr ""
#: templates/tags.html:6 templates/tags.html.py:30
msgid "Tag list"
-msgstr "Lista de etiquetas"
+msgstr ""
#: templates/tags.html:32
msgid "sorted alphabetically"
-msgstr "ordenar alfabéticamente"
+msgstr ""
#: templates/tags.html:32
msgid "by name"
-msgstr "por nombre"
+msgstr ""
#: templates/tags.html:33
msgid "sorted by frequency of tag use"
-msgstr "ordenar por frecuencia de uso de la etiqueta"
+msgstr ""
#: templates/tags.html:33
msgid "by popularity"
-msgstr "por popularidad"
+msgstr ""
#: templates/tags.html:39
msgid "All tags matching query"
-msgstr "Todas las etiquetas que coincidan con la busqueda"
+msgstr ""
#: templates/tags.html:39
msgid "all tags - make this empty in english"
-msgstr "todas las tags"
+msgstr ""
#: templates/tags.html:42
msgid "Nothing found"
-msgstr "Nada encontrado"
-
-#: templates/unanswered.html:7 templates/unanswered.html.py:18
-msgid "Unanswered questions"
-msgstr "Preguntas sin respuesta"
+msgstr ""
-#: templates/unanswered.html:97
+#: templates/unanswered.html:114
#, python-format
msgid "have %(num_q)s unanswered questions"
msgstr ""
-"<div class=\"questions-count\">%(num_q)s</div> preguntas <strong>sin "
-"respuesta</strong> "
#: templates/user_edit.html:6
msgid "Edit user profile"
-msgstr "Editar perfil de usuario"
+msgstr ""
#: templates/user_edit.html:19
msgid "edit profile"
-msgstr "editar perfil"
+msgstr ""
#: templates/user_edit.html:31
msgid "image associated with your email address"
-msgstr "imagen asociada con tu email"
+msgstr ""
#: templates/user_edit.html:31
-msgid "avatar"
-msgstr "avatar"
+#, python-format
+msgid "avatar, see %(gravatar_faq_url)s"
+msgstr ""
-#: templates/user_edit.html:36 templates/user_info.html:31
+#: templates/user_edit.html:36 templates/user_info.html:56
msgid "Registered user"
-msgstr "Usuario registrado"
+msgstr ""
-#: templates/user_edit.html:82
+#: templates/user_edit.html:86 templates/user_email_subscriptions.html:23
msgid "Update"
-msgstr "Actualización"
+msgstr ""
+
+#: templates/user_email_subscriptions.html:8
+msgid "Email subscription settings"
+msgstr ""
-#: templates/user_info.html:34
+#: templates/user_email_subscriptions.html:9
+msgid "email subscription settings info"
+msgstr ""
+
+#: templates/user_email_subscriptions.html:24
+msgid "Stop sending email"
+msgstr ""
+
+#: templates/user_info.html:22
+msgid "karma"
+msgstr ""
+
+#: templates/user_info.html:32
+msgid "Moderate this user"
+msgstr ""
+
+#: templates/user_info.html:45
msgid "update profile"
-msgstr "actualizar perfil de usuario"
+msgstr ""
-#: templates/user_info.html:40
+#: templates/user_info.html:60
msgid "real name"
-msgstr "nombre real"
+msgstr ""
-#: templates/user_info.html:45
+#: templates/user_info.html:65
msgid "member for"
-msgstr "miembro de"
+msgstr ""
-#: templates/user_info.html:50
+#: templates/user_info.html:70
msgid "last seen"
-msgstr "última vez visto"
+msgstr ""
-#: templates/user_info.html:56
+#: templates/user_info.html:76
msgid "user website"
-msgstr "sitio web del usuario"
+msgstr ""
-#: templates/user_info.html:62
+#: templates/user_info.html:82
msgid "location"
-msgstr "ubicación"
+msgstr ""
-#: templates/user_info.html:69
+#: templates/user_info.html:89
msgid "age"
-msgstr "edad"
+msgstr ""
-#: templates/user_info.html:70
+#: templates/user_info.html:90
msgid "age unit"
-msgstr "unidad de edad"
+msgstr ""
-#: templates/user_info.html:76
+#: templates/user_info.html:96
msgid "todays unused votes"
-msgstr "votos de hoy no usados"
+msgstr ""
-#: templates/user_info.html:77
+#: templates/user_info.html:97
msgid "votes left"
-msgstr "votos restantes"
-
-#: templates/user_preferences.html:10
-msgid "Connect with Twitter"
-msgstr "Conectar con Twitter"
-
-#: templates/user_preferences.html:13
-msgid "Twitter account name:"
-msgstr "Nombre de usuario en Twitter:"
-
-#: templates/user_preferences.html:15
-msgid "Twitter password:"
-msgstr "Contraseña de Twitter:"
-
-#: templates/user_preferences.html:17
-msgid "Send my Questions to Twitter"
-msgstr "Enviar mis preguntas a Twitter"
-
-#: templates/user_preferences.html:18
-msgid "Send my Answers to Twitter"
-msgstr "Enviar mis respuestas a Twitter"
+msgstr ""
-#: templates/user_preferences.html:19
-msgid "Save"
-msgstr "Guardar"
+#: templates/user_stats.html:12
+#, python-format
+msgid ""
+"\n"
+" <span class=\"count\">1</span> Question\n"
+" "
+msgid_plural ""
+"\n"
+" <span class=\"count\">%(counter)s</span> Questions\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
-#: templates/user_stats.html:16
-msgid "User questions"
-msgstr "Preguntas del usuario"
+#: templates/user_stats.html:23
+#, python-format
+msgid ""
+"\n"
+" <span class=\"count\">1</span> Answer\n"
+" "
+msgid_plural ""
+"\n"
+" <span class=\"count\">%(counter)s</span> Answers\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
-#: templates/user_stats.html:38
+#: templates/user_stats.html:36
#, python-format
msgid "the answer has been voted for %(vote_count)s times"
-msgstr "la respuesta ha sido votada %(vote_count)s veces"
+msgstr ""
-#: templates/user_stats.html:38
+#: templates/user_stats.html:36
msgid "this answer has been selected as correct"
-msgstr "esta respuesta ha sido seleccionada como correcta"
+msgstr ""
#: templates/user_stats.html:46
#, python-format
-msgid "the answer has been commented %(comment_count)s times"
-msgstr "la respuesta ha sido comentada %(comment_count)s veces"
+msgid ""
+"\n"
+" (one comment)\n"
+" "
+msgid_plural ""
+"\n"
+" the answer has been commented %(comment_count)s times\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/user_stats.html:61
+#, python-format
+msgid ""
+"\n"
+" <span class=\"count\">1</span> Vote\n"
+" "
+msgid_plural ""
+"\n"
+" <span class=\"count\">%(cnt)s</span> Votes\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
-#: templates/user_stats.html:60
-msgid "votes total"
-msgstr "votos totales"
+#: templates/user_stats.html:72
+msgid "thumb up"
+msgstr ""
-#: templates/user_stats.html:69
+#: templates/user_stats.html:73
msgid "user has voted up this many times"
-msgstr "el usuario ha votado positivo esta cantidad de veces"
+msgstr ""
-#: templates/user_stats.html:74
+#: templates/user_stats.html:77
+msgid "thumb down"
+msgstr ""
+
+#: templates/user_stats.html:78
msgid "user voted down this many times"
-msgstr "el usuario voto negativo esta cantidad de veces"
+msgstr ""
#: templates/user_stats.html:87
-msgid "Tags"
-msgstr "Etiquetas"
+#, python-format
+msgid ""
+"\n"
+" <span class=\"count\">1</span> Tag\n"
+" "
+msgid_plural ""
+"\n"
+" <span class=\"count\">%(counter)s</span> Tags\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
-#: templates/user_stats.html:97
+#: templates/user_stats.html:100
#, python-format
-msgid "see other questions tagged '%(tag)s' "
-msgstr "ver otras preguntas etiqueteadas '%(tag)s'"
+msgid ""
+"see other questions with %(view_user)s's contributions tagged '%(tag_name)s' "
+msgstr ""
+
+#: templates/user_stats.html:115
+#, python-format
+msgid ""
+"\n"
+" <span class=\"count\">1</span> Badge\n"
+" "
+msgid_plural ""
+"\n"
+" <span class=\"count\">%(counter)s</span> Badges\n"
+" "
+msgstr[0] ""
+msgstr[1] ""
#: templates/user_tabs.html:7
msgid "User profile"
-msgstr "Perfil de usuario"
+msgstr ""
#: templates/user_tabs.html:16
msgid "graph of user reputation"
-msgstr "gráfica de la reputación del usuario"
+msgstr ""
#: templates/user_tabs.html:17
msgid "reputation history"
-msgstr "historial de reputación"
+msgstr ""
+
+#: templates/user_tabs.html:23
+msgid "questions that user selected as his/her favorite"
+msgstr ""
#: templates/user_tabs.html:24
msgid "favorites"
-msgstr "favoritos"
-
-#: templates/user_tabs.html:28
-msgid "settings"
-msgstr "preferencias"
+msgstr ""
#: templates/users.html:6 templates/users.html.py:24
msgid "Users"
-msgstr "Usuarios"
+msgstr ""
#: templates/users.html:27
msgid "recent"
-msgstr "reciente"
+msgstr ""
#: templates/users.html:28
msgid "oldest"
-msgstr "más viejo"
+msgstr ""
#: templates/users.html:29
msgid "by username"
-msgstr "por nombre de usuario"
+msgstr ""
#: templates/users.html:35
#, python-format
msgid "users matching query %(suser)s:"
-msgstr "usuarios que coincidan con la busqueda %(suser)s:"
+msgstr ""
#: templates/users.html:39
msgid "Nothing found."
-msgstr "Nada encontrado."
+msgstr ""
#: templates/users_questions.html:11
msgid "this questions was selected as favorite"
-msgstr "esta pregunta ha sido seleccionada como favorita"
+msgstr ""
+
+#: templates/users_questions.html:12
+msgid "thumb-up on"
+msgstr ""
-#: templates/users_questions.html:33
+#: templates/users_questions.html:19
+msgid "thumb-up off"
+msgstr ""
+
+#: templates/users_questions.html:34
msgid "this answer has been accepted to be correct"
-msgstr "esta respuesta ha sido aceptada como correcta"
+msgstr ""
-#: templates/authopenid/changeemail.html:7
-#: templates/authopenid/changeemail.html:33
+#: templates/authopenid/changeemail.html:3
+#: templates/authopenid/changeemail.html:9
+#: templates/authopenid/changeemail.html:38
msgid "Change email"
-msgstr "Cambiar dirección email"
+msgstr ""
-#: templates/authopenid/changeemail.html:10
+#: templates/authopenid/changeemail.html:11
+msgid "Save your email address"
+msgstr ""
+
+#: templates/authopenid/changeemail.html:16
#, python-format
msgid "change %(email)s info"
-msgstr "Cambiar información del correo electrónico %(email)s"
+msgstr ""
-#: templates/authopenid/changeemail.html:13
-#: templates/authopenid/changeopenid.html:14
-#: templates/authopenid/changepw.html:19 templates/authopenid/delete.html:15
-#: templates/authopenid/delete.html:25
-msgid "Please correct errors below:"
-msgstr "Por favor corrija los errores debajo: "
+#: templates/authopenid/changeemail.html:18
+#, python-format
+msgid "here is why email is required, see %(gravatar_faq_url)s"
+msgstr ""
-#: templates/authopenid/changeemail.html:30
+#: templates/authopenid/changeemail.html:31
msgid "Your new Email"
-msgstr "Tu nuevo Email"
+msgstr ""
#: templates/authopenid/changeemail.html:31
-#: templates/authopenid/signin.html:136
-msgid "Password"
-msgstr "Contraseña"
+msgid "Your Email"
+msgstr ""
-#: templates/authopenid/changeemail.html:42
-#, fuzzy
+#: templates/authopenid/changeemail.html:38
+msgid "Save Email"
+msgstr ""
+
+#: templates/authopenid/changeemail.html:49
msgid "Validate email"
-msgstr "Cambiar dirección email"
+msgstr ""
-#: templates/authopenid/changeemail.html:45
+#: templates/authopenid/changeemail.html:52
#, python-format
-msgid "validate %(email)s info"
-msgstr "validar información de %(email)s "
+msgid "validate %(email)s info or go to %(change_email_url)s"
+msgstr ""
-#: templates/authopenid/changeemail.html:50
+#: templates/authopenid/changeemail.html:57
msgid "Email not changed"
-msgstr "Email no modificado."
+msgstr ""
-#: templates/authopenid/changeemail.html:53
+#: templates/authopenid/changeemail.html:60
#, python-format
-msgid "old %(email)s kept"
-msgstr "se ha conservado el viejo email %(email)s "
+msgid "old %(email)s kept, if you like go to %(change_email_url)s"
+msgstr ""
-#: templates/authopenid/changeemail.html:58
+#: templates/authopenid/changeemail.html:65
msgid "Email changed"
-msgstr "Email modificado."
+msgstr ""
-#: templates/authopenid/changeemail.html:61
+#: templates/authopenid/changeemail.html:68
#, python-format
msgid "your current %(email)s can be used for this"
-msgstr "tu email actual %(email)s puede ser usado para esto"
+msgstr ""
-#: templates/authopenid/changeemail.html:66
+#: templates/authopenid/changeemail.html:73
msgid "Email verified"
-msgstr "Email verificado"
+msgstr ""
-#: templates/authopenid/changeemail.html:69
+#: templates/authopenid/changeemail.html:76
msgid "thanks for verifying email"
-msgstr "gracias por verificar su correo"
+msgstr ""
-#: templates/authopenid/changeemail.html:74
+#: templates/authopenid/changeemail.html:81
msgid "email key not sent"
-msgstr "llave de correo no enviada"
+msgstr ""
-#: templates/authopenid/changeemail.html:77
+#: templates/authopenid/changeemail.html:84
#, python-format
msgid "email key not sent %(email)s change email here %(change_link)s"
-msgstr "email no enviado %(email)s cambiar email aquí %(change_link)s"
+msgstr ""
+
+#: templates/authopenid/changeopenid.html:4
+#: templates/authopenid/changeopenid.html:30
+#: templates/authopenid/settings.html:34
+msgid "Change OpenID"
+msgstr ""
#: templates/authopenid/changeopenid.html:8
msgid "Account: change OpenID URL"
-msgstr "Cuenta: cambiar la URL de OpenID"
+msgstr ""
#: templates/authopenid/changeopenid.html:12
msgid ""
"This is where you can change your OpenID URL. Make sure you remember it!"
-msgstr "Aquí es donde puedes cambiar tu OpenID URL. Asegurate de recordarla!"
+msgstr ""
+
+#: templates/authopenid/changeopenid.html:14
+#: templates/authopenid/delete.html:14 templates/authopenid/delete.html:24
+msgid "Please correct errors below:"
+msgstr ""
#: templates/authopenid/changeopenid.html:29
msgid "OpenID URL:"
-msgstr "URL de OpenID:"
+msgstr ""
-#: templates/authopenid/changeopenid.html:30
-msgid "Change OpenID"
-msgstr "Cambiar OpenID"
+#: templates/authopenid/changepw.html:5 templates/authopenid/changepw.html:14
+#: templates/authopenid/settings.html:29
+msgid "Change password"
+msgstr ""
-#: templates/authopenid/changepw.html:14
+#: templates/authopenid/changepw.html:7
msgid "Account: change password"
-msgstr "Cuenta: cambiar contraseña"
+msgstr ""
-#: templates/authopenid/changepw.html:17
+#: templates/authopenid/changepw.html:8
msgid "This is where you can change your password. Make sure you remember it!"
-msgstr "Aquí es donde puedes cambiar tu contraseña. Asegurate de recordarlo!"
-
-#: templates/authopenid/changepw.html:27
-msgid "Current password"
-msgstr "Contraseña actual"
-
-#: templates/authopenid/changepw.html:28
-msgid "New password"
-msgstr "Nueva contraseña"
-
-#: templates/authopenid/changepw.html:29
-msgid "New password again"
-msgstr "Nueva contraseña nuevamente"
-
-#: templates/authopenid/changepw.html:30 templates/authopenid/settings.html:29
-msgid "Change password"
-msgstr "Cambiar contraseña"
+msgstr ""
-#: templates/authopenid/complete.html:5
+#: templates/authopenid/complete.html:19
msgid "Connect your OpenID with this site"
-msgstr "Vincular tu OpenID con este sitio"
+msgstr ""
-#: templates/authopenid/complete.html:8
+#: templates/authopenid/complete.html:22
msgid "Connect your OpenID with your account on this site"
-msgstr "Vincular tu OpenID con tu cuenta en este sitio"
+msgstr ""
+
+#: templates/authopenid/complete.html:27
+#, python-format
+msgid "register new %(provider)s account info, see %(gravatar_faq_url)s"
+msgstr ""
+
+#: templates/authopenid/complete.html:31
+#, python-format
+msgid ""
+"%(username)s already exists, choose another name for \n"
+" %(provider)s. Email is required too, see %"
+"(gravatar_faq_url)s\n"
+" "
+msgstr ""
-#: templates/authopenid/complete.html:12
+#: templates/authopenid/complete.html:35
#, python-format
-msgid "register new %(provider)s account info"
-msgstr "Registrar una nueva cuenta %(provider)s."
+msgid ""
+"register new external %(provider)s account info, see %(gravatar_faq_url)s"
+msgstr ""
-#: templates/authopenid/complete.html:14
+#: templates/authopenid/complete.html:40
msgid "This account already exists, please use another."
-msgstr "Esta cuenta ya existe, por favor usar otra."
+msgstr ""
-#: templates/authopenid/complete.html:19 templates/authopenid/complete.html:32
-#: templates/authopenid/signin.html:120
+#: templates/authopenid/complete.html:55
msgid "Sorry, looks like we have some errors:"
-msgstr "Ups, parece que hay errores:"
+msgstr ""
-#: templates/authopenid/complete.html:47
+#: templates/authopenid/complete.html:76
msgid "Screen name label"
-msgstr "Nombre de Usuario"
+msgstr ""
-#: templates/authopenid/complete.html:48
+#: templates/authopenid/complete.html:83
msgid "Email address label"
-msgstr "Su email (correo electrónico)"
+msgstr ""
+
+#: templates/authopenid/complete.html:89 templates/authopenid/signup.html:15
+msgid "receive updates motivational blurb"
+msgstr ""
+
+#: templates/authopenid/complete.html:93
+msgid "Tag filter tool will be your right panel, once you log in."
+msgstr ""
-#: templates/authopenid/complete.html:49
+#: templates/authopenid/complete.html:95
msgid "create account"
-msgstr "crear cuenta"
+msgstr ""
-#: templates/authopenid/complete.html:56
+#: templates/authopenid/complete.html:104
msgid "Existing account"
-msgstr "Cuenta existente"
+msgstr ""
-#: templates/authopenid/complete.html:57
+#: templates/authopenid/complete.html:105
msgid "user name"
-msgstr "nombre de usuario"
+msgstr ""
-#: templates/authopenid/complete.html:58
+#: templates/authopenid/complete.html:106
msgid "password"
-msgstr "contraseña"
-
-#: templates/authopenid/complete.html:61
-msgid "Register"
-msgstr "Registrarse"
-
-#: templates/authopenid/complete.html:62 templates/authopenid/signin.html:138
-msgid "Forgot your password?"
-msgstr "¿Olvidaste tu contraseña?"
-
-#: templates/authopenid/confirm_email.txt:2
-msgid "Thank you for registering at our Q&A forum!"
msgstr ""
-#: templates/authopenid/confirm_email.txt:4
-msgid "Your account details are:"
+#: templates/authopenid/complete.html:111
+msgid "Register"
msgstr ""
-#: templates/authopenid/confirm_email.txt:6
-#: templates/authopenid/sendpw_email.txt:7
-msgid "Username:"
-msgstr "Nombre de usuario:"
-
-#: templates/authopenid/confirm_email.txt:7
-#: templates/authopenid/delete.html:20
-msgid "Password:"
-msgstr "Contraseña"
-
-#: templates/authopenid/confirm_email.txt:9
-msgid "Please sign in here:"
+#: templates/authopenid/complete.html:112 templates/authopenid/signin.html:140
+msgid "Forgot your password?"
msgstr ""
-#: templates/authopenid/confirm_email.txt:12
-#: templates/authopenid/email_validation.txt:14
-#: templates/authopenid/sendpw_email.txt:13
-msgid ""
-"Sincerely,\n"
-"Forum Administrator"
+#: templates/authopenid/delete.html:4 templates/authopenid/settings.html:38
+msgid "Delete account"
msgstr ""
-#: templates/authopenid/delete.html:9
+#: templates/authopenid/delete.html:8
msgid "Account: delete account"
-msgstr "Cuenta: borrar cuenta"
+msgstr ""
-#: templates/authopenid/delete.html:13
+#: templates/authopenid/delete.html:12
msgid ""
"Note: After deleting your account, anyone will be able to register this "
"username."
msgstr ""
-"Nota: Luego de borrar tu cuenta, cualquiera va a poder registrarse con este "
-"nombre de usuario."
-#: templates/authopenid/delete.html:17
+#: templates/authopenid/delete.html:16
msgid "Check confirm box, if you want delete your account."
-msgstr "Marca caja de confirmación, si deseas borrar tu cuenta."
+msgstr ""
-#: templates/authopenid/delete.html:32
+#: templates/authopenid/delete.html:19
+msgid "Password:"
+msgstr ""
+
+#: templates/authopenid/delete.html:31
msgid "I am sure I want to delete my account."
-msgstr "Estoy seguro que quiero borrar mi cuenta."
+msgstr ""
-#: templates/authopenid/delete.html:33
+#: templates/authopenid/delete.html:32
msgid "Password/OpenID URL"
-msgstr "Contraseña/OpenID URL"
+msgstr ""
-#: templates/authopenid/delete.html:33
+#: templates/authopenid/delete.html:32
msgid "(required for your security)"
-msgstr "(requerido por tu seguridad)"
-
-#: templates/authopenid/delete.html:35
-msgid "Delete account permanently"
-msgstr "Borrar la cuenta de forma permanente"
-
-#: templates/authopenid/email_validation.txt:2
-msgid "Greetings from the Q&A forum"
msgstr ""
-#: templates/authopenid/email_validation.txt:4
-msgid "To make use of the Forum, please follow the link below:"
+#: templates/authopenid/delete.html:34
+msgid "Delete account permanently"
msgstr ""
-#: templates/authopenid/email_validation.txt:8
-msgid "Following the link above will help us verify your email address."
+#: templates/authopenid/external_legacy_login_info.html:4
+#: templates/authopenid/external_legacy_login_info.html:7
+msgid "Traditional login information"
msgstr ""
-#: templates/authopenid/email_validation.txt:10
-msgid ""
-"If you beleive that this message was sent in mistake - \n"
-"no further action is needed. Just ingore this email, we apologize\n"
-"for any inconvenience"
+#: templates/authopenid/external_legacy_login_info.html:17
+msgid "how to login with password through external login website"
msgstr ""
-#: templates/authopenid/sendpw.html:4 templates/authopenid/sendpw.html.py:8
+#: templates/authopenid/sendpw.html:4 templates/authopenid/sendpw.html.py:7
msgid "Send new password"
-msgstr "Enviar nueva contraseña"
-
-#: templates/authopenid/sendpw.html:12
-msgid "Lost your password? No problem - here you can reset it."
-msgstr "¿Haz perdido tu contraseña? No hay problema - aquí puedes re-crearla."
-
-#: templates/authopenid/sendpw.html:13
-msgid ""
-"Please enter your username below and new password will be sent to your "
-"registered e-mail"
msgstr ""
-"Por favor, ingresa tu nombre de usuario y una nueva contraseña será enviada "
-"a la dirección de email registrada."
-
-#: templates/authopenid/sendpw.html:28
-msgid "User name"
-msgstr "Nombre de usuario"
-
-#: templates/authopenid/sendpw.html:30
-msgid "Reset password"
-msgstr "Re-crear contraseña"
-
-#: templates/authopenid/sendpw.html:30
-msgid "return to login"
-msgstr "volver a 'Ingresar'"
-#: templates/authopenid/sendpw.html:33
-msgid ""
-"Note: your new password will be activated only after you click the "
-"activation link in the email message"
+#: templates/authopenid/sendpw.html:10
+msgid "password recovery information"
msgstr ""
-"Nota: tu nueva contraseña solo será activada luego de que hagas click en el "
-"link de activación en el email enviado."
-#: templates/authopenid/sendpw_email.txt:2
-#, python-format
-msgid ""
-"Someone has requested to reset your password on %(site_url)s.\n"
-"If it were not you, it is safe to ignore this email."
+#: templates/authopenid/sendpw.html:21
+msgid "Reset password"
msgstr ""
-#: templates/authopenid/sendpw_email.txt:5
-msgid "Your new account details are:"
+#: templates/authopenid/sendpw.html:22
+msgid "return to login"
msgstr ""
-#: templates/authopenid/sendpw_email.txt:8
-#, fuzzy
-msgid "New password:"
-msgstr "Nueva contraseña:"
-
-#: templates/authopenid/sendpw_email.txt:10
-msgid "To confirm that you wanted to reset your password please visit:"
+#: templates/authopenid/settings.html:4
+msgid "Account functions"
msgstr ""
#: templates/authopenid/settings.html:30
msgid "Give your account a new password."
-msgstr "Crea una nueva contraseña para tu cuenta."
+msgstr ""
#: templates/authopenid/settings.html:31
msgid "Change email "
-msgstr "Cambiar email "
+msgstr ""
#: templates/authopenid/settings.html:32
msgid "Add or update the email address associated with your account."
-msgstr "Agrega o actualiza el email asociado a tu cuenta."
+msgstr ""
#: templates/authopenid/settings.html:35
msgid "Change openid associated to your account"
-msgstr "Cambia el OpenID asociado a tu cuenta"
-
-#: templates/authopenid/settings.html:38
-msgid "Delete account"
-msgstr "Eliminar cuenta"
+msgstr ""
#: templates/authopenid/settings.html:39
msgid "Erase your username and all your data from website"
-msgstr "Eliminar tu nombre de usuario y toda tu información del sitio"
+msgstr ""
-#: templates/authopenid/signin.html:4 templates/authopenid/signin.html:21
+#: templates/authopenid/signin.html:5 templates/authopenid/signin.html:21
msgid "User login"
-msgstr "Ingreso de usuario"
+msgstr ""
#: templates/authopenid/signin.html:28
#, python-format
@@ -2557,10 +3078,6 @@ msgid ""
"log in\n"
" "
msgstr ""
-"\n"
-" Tu respuesta a %(title)s %(summary)s será publicada una vez "
-"que ingreses \n"
-" "
#: templates/authopenid/signin.html:35
#, python-format
@@ -2569,127 +3086,85 @@ msgid ""
" %(title)s %(summary)s will be posted once you log in\n"
" "
msgstr ""
-"Tu pregunta \n"
-" %(title)s %(summary)s será publicada una vez que ingreses\n"
-" "
-#: templates/authopenid/signin.html:40
+#: templates/authopenid/signin.html:42
msgid "Click to sign in through any of these services."
-msgstr "Clickea para entrar por cualquiera de estos servicios."
+msgstr ""
-#: templates/authopenid/signin.html:103
+#: templates/authopenid/signin.html:117
msgid "Enter your <span id=\"enter_your_what\">Provider user name</span>"
-msgstr "Ingresa tu <span id=\"enter_your_what\">nombre de usuario</span>"
+msgstr ""
-#: templates/authopenid/signin.html:110
+#: templates/authopenid/signin.html:124
msgid ""
"Enter your <a class=\"openid_logo\" href=\"http://openid.net\">OpenID</a> "
"web address"
msgstr ""
-"Ingresa tu dirección (URL) de <a class=\"openid_logo\" href=\"http://openid."
-"net\">OpenID</a>"
-#: templates/authopenid/signin.html:112 templates/authopenid/signin.html:137
+#: templates/authopenid/signin.html:126 templates/authopenid/signin.html:138
msgid "Login"
-msgstr "Ingresar"
+msgstr ""
-#: templates/authopenid/signin.html:116
-msgid "we support two login modes"
-msgstr "soportamos dos tipos de ingreso"
+#: templates/authopenid/signin.html:129
+msgid "Enter your login name and password"
+msgstr ""
-#: templates/authopenid/signin.html:134
-msgid "Use login name and password"
-msgstr "Nombre de usuario y contraseña"
+#: templates/authopenid/signin.html:133
+msgid "Login name"
+msgstr ""
#: templates/authopenid/signin.html:135
-msgid "Login name"
-msgstr "Nombre de usuario"
+msgid "Password"
+msgstr ""
#: templates/authopenid/signin.html:139
-msgid "Create new account"
-msgstr "Crear cuenta nueva"
+msgid "Create account"
+msgstr ""
-#: templates/authopenid/signin.html:148
+#: templates/authopenid/signin.html:149
msgid "Why use OpenID?"
-msgstr "¿Porqué usar OpenID?"
+msgstr ""
-#: templates/authopenid/signin.html:151
+#: templates/authopenid/signin.html:152
msgid "with openid it is easier"
-msgstr "Con OpenID no necesitas crear un nuevo nombre de usuario y contraseña."
+msgstr ""
-#: templates/authopenid/signin.html:154
+#: templates/authopenid/signin.html:155
msgid "reuse openid"
msgstr ""
-"Puedes de forma segura re-usar el mismo nombre de usuario para todos los "
-"sitios que acepten OpenID."
-#: templates/authopenid/signin.html:157
+#: templates/authopenid/signin.html:158
msgid "openid is widely adopted"
msgstr ""
-"OpenID es extensamente usado. Hay más de 160,000,000 cuentas de OpenID en "
-"uso en el mundo. Mas de 10,000 sitios aceptan OpenID."
-#: templates/authopenid/signin.html:160
+#: templates/authopenid/signin.html:161
msgid "openid is supported open standard"
msgstr ""
-"OpenID es basado en un standard abierto, apoyado por muchas organizaciones."
-#: templates/authopenid/signin.html:165
+#: templates/authopenid/signin.html:166
msgid "Find out more"
-msgstr "Averigua más"
+msgstr ""
-#: templates/authopenid/signin.html:166
+#: templates/authopenid/signin.html:167
msgid "Get OpenID"
-msgstr "Adquiere una OpenID"
+msgstr ""
-#: templates/authopenid/signup.html:4 templates/authopenid/signup.html.py:8
+#: templates/authopenid/signup.html:4
msgid "Signup"
-msgstr "Registrate"
+msgstr ""
-#: templates/authopenid/signup.html:12
-msgid ""
-"We support two types of user registration: conventional username/password, "
-"and"
+#: templates/authopenid/signup.html:8
+msgid "Create login name and password"
msgstr ""
-"Soportamos dos formas de registro de usuario: convencional usuario/"
-"contraseña, y"
-#: templates/authopenid/signup.html:12
-msgid "the OpenID method"
-msgstr "OpenID"
+#: templates/authopenid/signup.html:10
+msgid "Traditional signup info"
+msgstr ""
#: templates/authopenid/signup.html:17
-msgid "Sorry, looks like we have some errors"
-msgstr "Ups, parece que hay errores."
-
-#: templates/authopenid/signup.html:35
-msgid "Conventional registration"
-msgstr "Registro clásico"
-
-#: templates/authopenid/signup.html:36
-msgid "choose a user name"
-msgstr "elije un nombre de usuario"
-
-#: templates/authopenid/signup.html:42
-msgid "back to login"
-msgstr "volver al ingreso de usuario"
-
-#: templates/authopenid/signup.html:46
-msgid "Register with your OpenID"
-msgstr "Registrate con tu OpenID"
-
-#: templates/authopenid/signup.html:49
-msgid "Login with your OpenID"
-msgstr "Ingresar con tu OpenID"
-
-#~ msgid "site title"
-#~ msgstr "Preguntalo.com.uy"
-
-#~ msgid "Have a total of"
-#~ msgstr "Hay un total de"
-
-#~ msgid "/account/"
-#~ msgstr "/cuenta/"
+msgid "Create Account"
+msgstr ""
-#~ msgid "content/"
-#~ msgstr "contenido/"
+#: templates/authopenid/signup.html:19
+msgid "return to OpenID login"
+msgstr ""
diff --git a/osqa.iml b/osqa.iml
new file mode 100755
index 00000000..f565f75f
--- /dev/null
+++ b/osqa.iml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+ <component name="FacetManager">
+ <facet type="django" name="Django">
+ <configuration>
+ <option name="rootFolder" value="$MODULE_DIR$" />
+ <option name="templatesFolder" value="$MODULE_DIR$/templates" />
+ <option name="settingsModule" value="settings.py" />
+ </configuration>
+ </facet>
+ </component>
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$" />
+ <orderEntry type="jdk" jdkName="Python 2.6.4" jdkType="Python SDK" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
+
diff --git a/session_messages/__init__.py b/session_messages/__init__.py
deleted file mode 100644
index 4dd10a6b..00000000
--- a/session_messages/__init__.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""
-Lightweight session-based messaging system.
-
-Time-stamp: <2009-03-10 19:22:29 carljm __init__.py>
-
-"""
-VERSION = (0, 1, 'pre')
-
-def create_message (request, message):
- """
- Create a message in the current session.
-
- """
- assert hasattr(request, 'session'), "django-session-messages requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
-
- try:
- request.session['messages'].append(message)
- except KeyError:
- request.session['messages'] = [message]
-
-def get_and_delete_messages (request, include_auth=False):
- """
- Get and delete all messages for current session.
-
- Optionally also fetches user messages from django.contrib.auth.
-
- """
- assert hasattr(request, 'session'), "django-session-messages requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
-
- messages = request.session.pop('messages', [])
- import logging
-
- if include_auth and request.user.is_authenticated():
- messages.extend(request.user.get_and_delete_messages())
-
- return messages
-
diff --git a/session_messages/context_processors.py b/session_messages/context_processors.py
deleted file mode 100644
index df9840fd..00000000
--- a/session_messages/context_processors.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-Context processor for lightweight session messages.
-
-Time-stamp: <2008-07-19 23:16:19 carljm context_processors.py>
-
-"""
-from django.utils.encoding import StrAndUnicode
-
-from session_messages import get_and_delete_messages
-
-def session_messages (request):
- """
- Returns session messages for the current session.
-
- """
- return { 'session_messages': LazyMessages(request) }
-
-class LazyMessages (StrAndUnicode):
- """
- Lazy message container, so messages aren't actually retrieved from
- session and deleted until the template asks for them.
-
- """
- def __init__(self, request):
- self.request = request
-
- def __iter__(self):
- return iter(self.messages)
-
- def __len__(self):
- return len(self.messages)
-
- def __nonzero__(self):
- return bool(self.messages)
-
- def __unicode__(self):
- return unicode(self.messages)
-
- def __getitem__(self, *args, **kwargs):
- return self.messages.__getitem__(*args, **kwargs)
-
- def _get_messages(self):
- if hasattr(self, '_messages'):
- return self._messages
- self._messages = get_and_delete_messages(self.request)
- return self._messages
- messages = property(_get_messages)
-
diff --git a/session_messages/models.py b/session_messages/models.py
deleted file mode 100644
index b67ead6d..00000000
--- a/session_messages/models.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-blank models.py
-"""
diff --git a/settings.py b/settings.py
index 087c9fd8..bbbd2f5b 100755
--- 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.skins.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
@@ -23,9 +24,9 @@ MIDDLEWARE_CLASSES = [
#'django.middleware.cache.FetchFromCacheMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
#'django.middleware.sqlprint.SqlPrintingMiddleware',
- 'middleware.anon_user.ConnectToSessionMessagesMiddleware',
- 'middleware.pagesize.QuestionsPageSizeMiddleware',
- 'middleware.cancel.CancelActionMiddleware',
+ 'forum.middleware.anon_user.ConnectToSessionMessagesMiddleware',
+ 'forum.middleware.pagesize.QuestionsPageSizeMiddleware',
+ 'forum.middleware.cancel.CancelActionMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
'recaptcha_django.middleware.ReCaptchaMiddleware',
'django.middleware.transaction.TransactionMiddleware',
@@ -35,14 +36,14 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'context.application_settings',
#'django.core.context_processors.i18n',
- 'user_messages.context_processors.user_messages',#must be before auth
+ 'forum.user_messages.context_processors.user_messages',#must be before auth
'django.core.context_processors.auth', #this is required for admin
)
ROOT_URLCONF = 'urls'
TEMPLATE_DIRS = (
- os.path.join(os.path.dirname(__file__), 'templates').replace('\\','/'),
+ os.path.join(os.path.dirname(__file__),'forum','skins').replace('\\','/'),
)
#UPLOAD SETTINGS
@@ -69,7 +70,6 @@ INSTALLED_APPS = [
'forum',
'django_authopenid',
'debug_toolbar' ,
- 'user_messages',
]
AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend',]
@@ -80,7 +80,7 @@ if USE_SPHINX_SEARCH:
if USE_FB_CONNECT:
INSTALLED_APPS.append('fbconnect')
-if DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ):
+if DATABASE_ENGINE in ('postgresql_psycopg2', 'postgresql', ) and False:#todo - is this always false?
USE_PG_FTS = True
INSTALLED_APPS.append('pgfulltext')
else:
diff --git a/settings_local.py.dist b/settings_local.py.dist
index fb96e461..766c1a38 100755
--- a/settings_local.py.dist
+++ b/settings_local.py.dist
@@ -4,12 +4,12 @@ from django.utils.translation import ugettext as _
def check_local_setting(name, value):
local_vars = locals()
- if name in local_vars and local_var[name] == value:
+ if name in local_vars and local_vars[name] == value:
return True
else:
return False
-SITE_SRC_ROOT = os.path.dirname(__file__)
+SITE_SRC_ROOT = os.path.dirname(__file__)
LOG_FILENAME = 'django.osqa.log'
#for logging
@@ -25,14 +25,14 @@ ADMINS = (('Forum Admin', 'forum@example.com'),)
MANAGERS = ADMINS
#DEBUG SETTINGS
-DEBUG = False
+DEBUG = False
TEMPLATE_DEBUG = DEBUG
INTERNAL_IPS = ('127.0.0.1',)
-DATABASE_NAME = 'osqa' # Or path to database file if using sqlite3.
-DATABASE_USER = 'osqa' # Not used with sqlite3.
+DATABASE_NAME = '' # Or path to database file if using sqlite3.
+DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
-DATABASE_ENGINE = 'mysql' #mysql, etc
+DATABASE_ENGINE = '' #mysql, etc
DATABASE_HOST = ''
DATABASE_PORT = ''
@@ -78,8 +78,8 @@ EMAIL_UNIQUE = False
APP_URL = 'http://osqa.net' #used by email notif system and RSS
GOOGLE_SITEMAP_CODE = ''
GOOGLE_ANALYTICS_KEY = ''
-BOOKS_ON = False
-WIKI_ON = True
+BOOKS_ON = False
+WIKI_ON = True
USE_EXTERNAL_LEGACY_LOGIN = False
EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.net'
EXTERNAL_LEGACY_LOGIN_PORT = 80
@@ -94,7 +94,7 @@ USE_SPHINX_SEARCH = False #if True all SPHINX_* settings are required
#also sphinx search engine and djangosphinxs app must be installed
#sample sphinx configuration file is /sphinx/sphinx.conf
SPHINX_API_VERSION = 0x113 #refer to djangosphinx documentation
-SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
+SPHINX_SEARCH_INDICES=('osqa',) #a tuple of index names remember about a comma after the
#last item, especially if you have just one :)
SPHINX_SERVER='localhost'
SPHINX_PORT=3312
@@ -102,8 +102,9 @@ SPHINX_PORT=3312
#please get these at recaptcha.net
RECAPTCHA_PRIVATE_KEY='...'
RECAPTCHA_PUBLIC_KEY='...'
+OSQA_DEFAULT_SKIN = 'default'
#Facebook settings
-USE_FB_CONNECT=True
+USE_FB_CONNECT=False #make this True and fill out keys below
FB_API_KEY='' #your api key from facebook
FB_SECRET='' #your application secret
diff --git a/sql_scripts/update_2010_02_22.sql b/sql_scripts/update_2010_02_22.sql
new file mode 100644
index 00000000..2778885a
--- /dev/null
+++ b/sql_scripts/update_2010_02_22.sql
@@ -0,0 +1 @@
+alter table answer add column deleted_at datetime;
diff --git a/templates/content/js/wmd/wmd-min.js b/templates/content/js/wmd/wmd-min.js
deleted file mode 100644
index 85cbd907..00000000
--- a/templates/content/js/wmd/wmd-min.js
+++ /dev/null
@@ -1 +0,0 @@
-var Attacklab=Attacklab||{};Attacklab.wmdBase=function(){var y=top.Attacklab;var E=top.document;var s=top.RegExp;var l=top.navigator;y.Util={};y.Position={};y.Command={};y.Global={};var a=y.Util;var C=y.Position;var h=y.Command;var v=y.Global;v.isIE=/msie/.test(l.userAgent.toLowerCase());v.isIE_5or6=/msie 6/.test(l.userAgent.toLowerCase())||/msie 5/.test(l.userAgent.toLowerCase());v.isIE_7plus=v.isIE&&!v.isIE_5or6;v.isOpera=/opera/.test(l.userAgent.toLowerCase());v.isKonqueror=/konqueror/.test(l.userAgent.toLowerCase());var c="粗体 <strong> Ctrl-B";var f="斜体 <em> Ctrl-I";var z="超链接 <a> Ctrl-L";var u="引用 <blockquote> Ctrl-.";var e="代码 <pre><code> Ctrl-K";var d="图片 <img> Ctrl-G";var q="数字编号列表 <ol> Ctrl-O";var t="项目符号列表 <ul> Ctrl-U";var i="标题 <h1>/<h2> Ctrl-H";var p="水平线 <hr> Ctrl-R";var m="撤销 Ctrl-Z";var j="重做 Ctrl-Y";var B="<p style='margin-top: 0px'><b>输入图片地址</b></p><p>示例:<br />http://www.cnprog.com/images/temp.jpg \"我的截图\"</p>";var D="<p style='margin-top: 0px'><b>输入Web地址</b></p><p>示例:<br />http://www.cnprog.com/ \"我的网站\"</p>";var n='<div>或者上传本地图片:</div><input type="file" name="file-upload" id="file-upload" size="26" onchange="return ajaxFileUpload($(\'#image-url\'));"/><br><img id="loading" src="/content/images/indicator.gif" style="display:none;"/>';var b="http://";var g="http://";var o="images/";var A=500;var x=100;var k="http://wmd-editor.com/";var r="WMD website";var w="_blank";y.PanelCollection=function(){this.buttonBar=E.getElementById("wmd-button-bar");this.preview=E.getElementById("previewer");this.output=E.getElementById("wmd-output");this.input=E.getElementById("editor")};y.panels=undefined;y.ieCachedRange=null;y.ieRetardedClick=false;a.isVisible=function(F){if(window.getComputedStyle){return window.getComputedStyle(F,null).getPropertyValue("display")!=="none"}else{if(F.currentStyle){return F.currentStyle.display!=="none"}}};a.addEvent=function(G,F,H){if(G.attachEvent){G.attachEvent("on"+F,H)}else{G.addEventListener(F,H,false)}};a.removeEvent=function(G,F,H){if(G.detachEvent){G.detachEvent("on"+F,H)}else{G.removeEventListener(F,H,false)}};a.fixEolChars=function(F){F=F.replace(/\r\n/g,"\n");F=F.replace(/\r/g,"\n");return F};a.extendRegExp=function(H,J,G){if(J===null||J===undefined){J=""}if(G===null||G===undefined){G=""}var I=H.toString();var F;I=I.replace(/\/([gim]*)$/,"");F=s.$1;I=I.replace(/(^\/|\/$)/g,"");I=J+I+G;return new s(I,F)};a.createImage=function(F){var H=o+F;var G=E.createElement("img");G.className="wmd-button";G.src=H;return G};a.prompt=function(M,P,H){var I;var F;var K;var J=0;if(arguments.length==4){J=arguments[3]}if(P===undefined){P=""}var L=function(Q){var R=(Q.charCode||Q.keyCode);if(R===27){N(true)}};var N=function(Q){a.removeEvent(E.body,"keydown",L);var R=K.value;if(Q){R=null}else{R=R.replace("http://http://","http://");R=R.replace("http://https://","https://");R=R.replace("http://ftp://","ftp://");if(R.indexOf("http://")===-1&&R.indexOf("ftp://")===-1){R="http://"+R}}I.parentNode.removeChild(I);F.parentNode.removeChild(F);H(R);return false};var G=function(){F=E.createElement("div");F.className="wmd-prompt-background";style=F.style;style.position="absolute";style.top="0";style.zIndex="1000";if(v.isKonqueror){style.backgroundColor="transparent"}else{if(v.isIE){style.filter="alpha(opacity=50)"}else{style.opacity="0.5"}}var Q=C.getPageSize();style.height=Q[1]+"px";if(v.isIE){style.left=E.documentElement.scrollLeft;style.width=E.documentElement.clientWidth}else{style.left="0";style.width="100%"}E.body.appendChild(F)};var O=function(){I=E.createElement("div");I.className="wmd-prompt-dialog";I.style.padding="10px;";I.style.position="fixed";I.style.width="400px";I.style.zIndex="1001";var Q=E.createElement("div");Q.innerHTML=M;Q.style.padding="5px";I.appendChild(Q);var S=E.createElement("form");S.onsubmit=function(){return N(false)};style=S.style;style.padding="0";style.margin="0";style.cssFloat="left";style.width="100%";style.textAlign="center";style.position="relative";I.appendChild(S);K=E.createElement("input");if(J==1){K.id="image-url"}K.type="text";K.value=P;style=K.style;style.display="block";style.width="80%";style.marginLeft=style.marginRight="auto";S.appendChild(K);if(J==1){var R=E.createElement("div");R.innerHTML=n;R.style.padding="5px";S.appendChild(R)}var U=E.createElement("input");U.type="button";U.onclick=function(){return N(false)};U.value="OK";style=U.style;style.margin="10px";style.display="inline";style.width="7em";var T=E.createElement("input");T.type="button";T.onclick=function(){return N(true)};T.value="Cancel";style=T.style;style.margin="10px";style.display="inline";style.width="7em";if(/mac/.test(l.platform.toLowerCase())){S.appendChild(T);S.appendChild(U)}else{S.appendChild(U);S.appendChild(T)}a.addEvent(E.body,"keydown",L);I.style.top="50%";I.style.left="50%";I.style.display="block";if(v.isIE_5or6){I.style.position="absolute";I.style.top=E.documentElement.scrollTop+200+"px";I.style.left="50%"}E.body.appendChild(I);I.style.marginTop=-(C.getHeight(I)/2)+"px";I.style.marginLeft=-(C.getWidth(I)/2)+"px"};G();top.setTimeout(function(){O();var R=P.length;if(K.selectionStart!==undefined){K.selectionStart=0;K.selectionEnd=R}else{if(K.createTextRange){var Q=K.createTextRange();Q.collapse(false);Q.moveStart("character",-R);Q.moveEnd("character",R);Q.select()}}K.focus()},0)};C.getTop=function(H,G){var F=H.offsetTop;if(!G){while(H=H.offsetParent){F+=H.offsetTop}}return F};C.getHeight=function(F){return F.offsetHeight||F.scrollHeight};C.getWidth=function(F){return F.offsetWidth||F.scrollWidth};C.getPageSize=function(){var G,H;var F,K;if(self.innerHeight&&self.scrollMaxY){G=E.body.scrollWidth;H=self.innerHeight+self.scrollMaxY}else{if(E.body.scrollHeight>E.body.offsetHeight){G=E.body.scrollWidth;H=E.body.scrollHeight}else{G=E.body.offsetWidth;H=E.body.offsetHeight}}if(self.innerHeight){F=self.innerWidth;K=self.innerHeight}else{if(E.documentElement&&E.documentElement.clientHeight){F=E.documentElement.clientWidth;K=E.documentElement.clientHeight}else{if(E.body){F=E.body.clientWidth;K=E.body.clientHeight}}}var J=Math.max(G,F);var I=Math.max(H,K);return[J,I,F,K]};y.inputPoller=function(O,H){var F=this;var K=y.panels.input;var G;var I;var L;var J;this.tick=function(){if(!a.isVisible(K)){return}if(K.selectionStart||K.selectionStart===0){var Q=K.selectionStart;var P=K.selectionEnd;if(Q!=G||P!=I){G=Q;I=P;if(L!=K.value){L=K.value;return true}}}return false};var N=function(){if(!a.isVisible(K)){return}if(F.tick()){O()}};var M=function(){J=top.setInterval(N,H)};this.destroy=function(){top.clearInterval(J)};M()};y.undoManager=function(Q){var U=this;var O=[];var M=0;var L="none";var G;var R;var H;var K;var F=function(W,V){if(L!=W){L=W;if(!V){I()}}if(!v.isIE||L!="moving"){H=top.setTimeout(N,1)}else{K=null}};var N=function(){K=new y.TextareaState();R.tick();H=undefined};this.setCommandMode=function(){L="command";I();H=top.setTimeout(N,0)};this.canUndo=function(){return M>1};this.canRedo=function(){if(O[M+1]){return true}return false};this.undo=function(){if(U.canUndo()){if(G){G.restore();G=null}else{O[M]=new y.TextareaState();O[--M].restore();if(Q){Q()}}}L="none";y.panels.input.focus();N()};this.redo=function(){if(U.canRedo()){O[++M].restore();if(Q){Q()}}L="none";y.panels.input.focus();N()};var I=function(){var V=K||new y.TextareaState();if(!V){return false}if(L=="moving"){if(!G){G=V}return}if(G){if(O[M-1].text!=G.text){O[M++]=G}G=null}O[M++]=V;O[M+1]=null;if(Q){Q()}};var P=function(V){var X=false;if(V.ctrlKey||V.metaKey){var W=V.charCode||V.keyCode;var Y=String.fromCharCode(W);switch(Y){case"y":U.redo();X=true;break;case"z":if(!V.shiftKey){U.undo()}else{U.redo()}X=true;break}}if(X){if(V.preventDefault){V.preventDefault()}if(top.event){top.event.returnValue=false}return}};var T=function(V){if(!V.ctrlKey&&!V.metaKey){var W=V.keyCode;if((W>=33&&W<=40)||(W>=63232&&W<=63235)){F("moving")}else{if(W==8||W==46||W==127){F("deleting")}else{if(W==13){F("newlines")}else{if(W==27){F("escape")}else{if((W<16||W>20)&&W!=91){F("typing")}}}}}}};var J=function(){a.addEvent(y.panels.input,"keypress",function(W){if((W.ctrlKey||W.metaKey)&&(W.keyCode==89||W.keyCode==90)){W.preventDefault()}});var V=function(){if(v.isIE||(K&&K.text!=y.panels.input.value)){if(H==undefined){L="paste";I();N()}}};R=new y.inputPoller(V,x);a.addEvent(y.panels.input,"keydown",P);a.addEvent(y.panels.input,"keydown",T);a.addEvent(y.panels.input,"mousedown",function(){F("moving")});y.panels.input.onpaste=V;y.panels.input.ondrop=V};var S=function(){J();N();I()};this.destroy=function(){if(R){R.destroy()}};S()};y.editor=function(O){if(!O){O=function(){}}var L=y.panels.input;var I=0;var P=this;var K;var R;var G;var M;var N;var U=function(W){L.focus();if(W.textOp){if(N){N.setCommandMode()}var Y=new y.TextareaState();if(!Y){return}var Z=Y.getChunks();var V=function(){L.focus();if(Z){Y.setChunks(Z)}Y.restore();O()};var X=W.textOp(Z,V);if(!X){V()}}if(W.execute){W.execute(P)}};var S=function(){if(N){F(document.getElementById("wmd-undo-button"),N.canUndo());F(document.getElementById("wmd-redo-button"),N.canRedo())}};var F=function(V,X){var Y="0px";var Z="-20px";var W="-40px";if(X){V.style.backgroundPosition=V.XShift+" "+Y;V.onmouseover=function(){this.style.backgroundPosition=this.XShift+" "+W};V.onmouseout=function(){this.style.backgroundPosition=this.XShift+" "+Y};if(v.isIE){V.onmousedown=function(){y.ieRetardedClick=true;y.ieCachedRange=document.selection.createRange()}}if(!V.isHelp){V.onclick=function(){if(this.onmouseout){this.onmouseout()}U(this);return false}}}else{V.style.backgroundPosition=V.XShift+" "+Z;V.onmouseover=V.onmouseout=V.onclick=function(){}}};var J=function(){var Z=document.getElementById("wmd-button-bar");var W="0px";var Y="-20px";var ae="-40px";var ak=document.createElement("ul");ak.id="wmd-button-row";ak=Z.appendChild(ak);var ad=document.createElement("li");ad.className="wmd-button";ad.id="wmd-bold-button";ad.title=c;ad.XShift="0px";ad.textOp=h.doBold;F(ad,true);ak.appendChild(ad);var ac=document.createElement("li");ac.className="wmd-button";ac.id="wmd-italic-button";ac.title=f;ac.XShift="-20px";ac.textOp=h.doItalic;F(ac,true);ak.appendChild(ac);var ah=document.createElement("li");ah.className="wmd-spacer";ah.id="wmd-spacer1";ak.appendChild(ah);var ai=document.createElement("li");ai.className="wmd-button";ai.id="wmd-link-button";ai.title=z;ai.XShift="-40px";ai.textOp=function(ap,aq){return h.doLinkOrImage(ap,aq,false)};F(ai,true);ak.appendChild(ai);var al=document.createElement("li");al.className="wmd-button";al.id="wmd-quote-button";al.title=u;al.XShift="-60px";al.textOp=h.doBlockquote;F(al,true);ak.appendChild(al);var am=document.createElement("li");am.className="wmd-button";am.id="wmd-code-button";am.title=e;am.XShift="-80px";am.textOp=h.doCode;F(am,true);ak.appendChild(am);var aa=document.createElement("li");aa.className="wmd-button";aa.id="wmd-image-button";aa.title=d;aa.XShift="-100px";aa.textOp=function(ap,aq){return h.doLinkOrImage(ap,aq,true)};F(aa,true);ak.appendChild(aa);var ag=document.createElement("li");ag.className="wmd-spacer";ag.id="wmd-spacer2";ak.appendChild(ag);var ab=document.createElement("li");ab.className="wmd-button";ab.id="wmd-olist-button";ab.title=q;ab.XShift="-120px";ab.textOp=function(ap,aq){h.doList(ap,aq,true)};F(ab,true);ak.appendChild(ab);var ao=document.createElement("li");ao.className="wmd-button";ao.id="wmd-ulist-button";ao.title=t;ao.XShift="-140px";ao.textOp=function(ap,aq){h.doList(ap,aq,false)};F(ao,true);ak.appendChild(ao);var aj=document.createElement("li");aj.className="wmd-button";aj.id="wmd-heading-button";aj.title=i;aj.XShift="-160px";aj.textOp=h.doHeading;F(aj,true);ak.appendChild(aj);var X=document.createElement("li");X.className="wmd-button";X.id="wmd-hr-button";X.title=p;X.XShift="-180px";X.textOp=h.doHorizontalRule;F(X,true);ak.appendChild(X);var af=document.createElement("li");af.className="wmd-spacer";af.id="wmd-spacer3";ak.appendChild(af);var V=document.createElement("li");V.className="wmd-button";V.id="wmd-undo-button";V.title=m;V.XShift="-200px";V.execute=function(ap){ap.undo()};F(V,true);ak.appendChild(V);var an=document.createElement("li");an.className="wmd-button";an.id="wmd-redo-button";an.title=j;if(/win/.test(l.platform.toLowerCase())){an.title=j}else{an.title="重做 - Ctrl+Shift+Z"}an.XShift="-220px";an.execute=function(ap){ap.redo()};F(an,true);ak.appendChild(an);S()};var H=function(){if(/\?noundo/.test(E.location.href)){y.nativeUndo=true}if(!y.nativeUndo){N=new y.undoManager(function(){O();S()})}J();var W="keydown";if(v.isOpera){W="keypress"}a.addEvent(L,W,function(Y){if(Y.ctrlKey||Y.metaKey){var Z=Y.charCode||Y.keyCode;var X=String.fromCharCode(Z).toLowerCase();if(Z===46){X=""}if(Z===190){X="."}switch(X){case"b":U(document.getElementById("wmd-bold-button"));break;case"i":U(document.getElementById("wmd-italic-button"));break;case"l":U(document.getElementById("wmd-link-button"));break;case".":U(document.getElementById("wmd-quote-button"));break;case"k":U(document.getElementById("wmd-code-button"));break;case"g":U(document.getElementById("wmd-image-button"));break;case"o":U(document.getElementById("wmd-olist-button"));break;case"u":U(document.getElementById("wmd-ulist-button"));break;case"h":U(document.getElementById("wmd-heading-button"));break;case"r":U(document.getElementById("wmd-hr-button"));break;case"y":U(document.getElementById("wmd-redo-button"));break;case"z":if(Y.shiftKey){U(document.getElementById("wmd-redo-button"))}else{U(document.getElementById("wmd-undo-button"))}break;default:return}if(Y.preventDefault){Y.preventDefault()}if(top.event){top.event.returnValue=false}}});a.addEvent(L,"keyup",function(X){if(X.shiftKey&&!X.ctrlKey&&!X.metaKey){var Y=X.charCode||X.keyCode;if(Y===13){fakeButton={};fakeButton.textOp=h.doAutoindent;U(fakeButton)}}});if(L.form){var V=L.form.onsubmit;L.form.onsubmit=function(){Q();if(V){return V.apply(this,arguments)}}}};var Q=function(){if(y.showdown){var V=new y.showdown.converter()}var W=L.value;var X=function(){L.value=W};if(!/markdown/.test(y.wmd_env.output.toLowerCase())){if(V){L.value=V.makeHtml(W);top.setTimeout(X,0)}}return true};this.undo=function(){if(N){N.undo()}};this.redo=function(){if(N){N.redo()}};var T=function(){H()};this.destroy=function(){if(N){N.destroy()}if(G.parentNode){G.parentNode.removeChild(G)}if(L){L.style.marginTop=""}top.clearInterval(M)};T()};y.TextareaState=function(){var F=this;var G=y.panels.input;this.init=function(){if(!a.isVisible(G)){return}this.setInputAreaSelectionStartEnd();this.scrollTop=G.scrollTop;if(!this.text&&G.selectionStart||G.selectionStart===0){this.text=G.value}};this.setInputAreaSelection=function(){if(!a.isVisible(G)){return}if(G.selectionStart!==undefined&&!v.isOpera){G.focus();G.selectionStart=F.start;G.selectionEnd=F.end;G.scrollTop=F.scrollTop}else{if(E.selection){if(E.activeElement&&E.activeElement!==G){return}G.focus();var H=G.createTextRange();H.moveStart("character",-G.value.length);H.moveEnd("character",-G.value.length);H.moveEnd("character",F.end);H.moveStart("character",F.start);H.select()}}};this.setInputAreaSelectionStartEnd=function(){if(G.selectionStart||G.selectionStart===0){F.start=G.selectionStart;F.end=G.selectionEnd}else{if(E.selection){F.text=a.fixEolChars(G.value);var K;if(y.ieRetardedClick&&y.ieCachedRange){K=y.ieCachedRange;y.ieRetardedClick=false}else{K=E.selection.createRange()}var L=a.fixEolChars(K.text);var J="\x07";var I=J+L+J;K.text=I;var M=a.fixEolChars(G.value);K.moveStart("character",-I.length);K.text=L;F.start=M.indexOf(J);F.end=M.lastIndexOf(J)-J.length;var H=F.text.length-a.fixEolChars(G.value).length;if(H){K.moveStart("character",-L.length);while(H--){L+="\n";F.end+=1}K.text=L}this.setInputAreaSelection()}}};this.restore=function(){if(F.text!=undefined&&F.text!=G.value){G.value=F.text}this.setInputAreaSelection();G.scrollTop=F.scrollTop};this.getChunks=function(){var H=new y.Chunks();H.before=a.fixEolChars(F.text.substring(0,F.start));H.startTag="";H.selection=a.fixEolChars(F.text.substring(F.start,F.end));H.endTag="";H.after=a.fixEolChars(F.text.substring(F.end));H.scrollTop=F.scrollTop;return H};this.setChunks=function(H){H.before=H.before+H.startTag;H.after=H.endTag+H.after;if(v.isOpera){H.before=H.before.replace(/\n/g,"\r\n");H.selection=H.selection.replace(/\n/g,"\r\n");H.after=H.after.replace(/\n/g,"\r\n")}this.start=H.before.length;this.end=H.before.length+H.selection.length;this.text=H.before+H.selection+H.after;this.scrollTop=H.scrollTop};this.init()};y.Chunks=function(){};y.Chunks.prototype.findTags=function(G,I){var F=this;var H;if(G){H=a.extendRegExp(G,"","$");this.before=this.before.replace(H,function(J){F.startTag=F.startTag+J;return""});H=a.extendRegExp(G,"^","");this.selection=this.selection.replace(H,function(J){F.startTag=F.startTag+J;return""})}if(I){H=a.extendRegExp(I,"","$");this.selection=this.selection.replace(H,function(J){F.endTag=J+F.endTag;return""});H=a.extendRegExp(I,"^","");this.after=this.after.replace(H,function(J){F.endTag=J+F.endTag;return""})}};y.Chunks.prototype.trimWhitespace=function(F){this.selection=this.selection.replace(/^(\s*)/,"");if(!F){this.before+=s.$1}this.selection=this.selection.replace(/(\s*)$/,"");if(!F){this.after=s.$1+this.after}};y.Chunks.prototype.skipLines=function(H,G,F){if(H===undefined){H=1}if(G===undefined){G=1}H++;G++;var I;var J;this.selection=this.selection.replace(/(^\n*)/,"");this.startTag=this.startTag+s.$1;this.selection=this.selection.replace(/(\n*$)/,"");this.endTag=this.endTag+s.$1;this.startTag=this.startTag.replace(/(^\n*)/,"");this.before=this.before+s.$1;this.endTag=this.endTag.replace(/(\n*$)/,"");this.after=this.after+s.$1;if(this.before){I=J="";while(H--){I+="\\n?";J+="\n"}if(F){I="\\n*"}this.before=this.before.replace(new s(I+"$",""),J)}if(this.after){I=J="";while(G--){I+="\\n?";J+="\n"}if(F){I="\\n*"}this.after=this.after.replace(new s(I,""),J)}};h.prefixes="(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)";h.unwrap=function(G){var F=new s("([^\\n])\\n(?!(\\n|"+h.prefixes+"))","g");G.selection=G.selection.replace(F,"$1 $2")};h.wrap=function(G,F){h.unwrap(G);var H=new s("(.{1,"+F+"})( +|$\\n?)","gm");G.selection=G.selection.replace(H,function(I,J){if(new s("^"+h.prefixes,"").test(I)){return I}return J+"\n"});G.selection=G.selection.replace(/\s+$/,"")};h.doBold=function(F,G){return h.doBorI(F,G,2,"strong text")};h.doItalic=function(F,G){return h.doBorI(F,G,1,"emphasized text")};h.doBorI=function(L,J,K,F){L.trimWhitespace();L.selection=L.selection.replace(/\n{2,}/g,"\n");L.before.search(/(\**$)/);var I=s.$1;L.after.search(/(^\**)/);var G=s.$1;var M=Math.min(I.length,G.length);if((M>=K)&&(M!=2||K!=1)){L.before=L.before.replace(s("[*]{"+K+"}$",""),"");L.after=L.after.replace(s("^[*]{"+K+"}",""),"")}else{if(!L.selection&&G){L.after=L.after.replace(/^([*_]*)/,"");L.before=L.before.replace(/(\s?)$/,"");var H=s.$1;L.before=L.before+G+H}else{if(!L.selection&&!G){L.selection=F}var N=K<=1?"*":"**";L.before=L.before+N;L.after=N+L.after}}return};h.stripLinkDefs=function(G,F){G=G.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,function(K,L,H,I,J){F[L]=K.replace(/\s*$/,"");if(I){F[L]=K.replace(/["(](.+?)[")]$/,"");return I+J}return""});return G};h.addLinkDef=function(M,I){var F=0;var H={};M.before=h.stripLinkDefs(M.before,H);M.selection=h.stripLinkDefs(M.selection,H);M.after=h.stripLinkDefs(M.after,H);var G="";var L=/(\[(?:\[[^\]]*\]|[^\[\]])*\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g;var K=function(O){F++;O=O.replace(/^[ ]{0,3}\[(\d+)\]:/," ["+F+"]:");G+="\n"+O};var J=function(P,Q,R,O){if(H[R]){K(H[R]);return Q+F+O}return P};M.before=M.before.replace(L,J);if(I){K(I)}else{M.selection=M.selection.replace(L,J)}var N=F;M.after=M.after.replace(L,J);if(M.after){M.after=M.after.replace(/\n*$/,"")}if(!M.after){M.selection=M.selection.replace(/\n*$/,"")}M.after+="\n\n"+G;return N};h.doLinkOrImage=function(F,G,I){F.trimWhitespace();F.findTags(/\s*!?\[/,/\][ ]?(?:\n[ ]*)?(\[.*?\])?/);if(F.endTag.length>1){F.startTag=F.startTag.replace(/!?\[/,"");F.endTag="";h.addLinkDef(F,null)}else{if(/\n\n/.test(F.selection)){h.addLinkDef(F,null);return}var H=function(L){if(L!==null){F.startTag=F.endTag="";var K=" [999]: "+L;var J=h.addLinkDef(F,K);F.startTag=I?"![":"[";F.endTag="]["+J+"]";if(!F.selection){if(I){F.selection="alt text"}else{F.selection="link text"}}}G()};if(I){a.prompt(B,b,H,1)}else{a.prompt(D,g,H)}return true}};a.makeAPI=function(){y.wmd={};y.wmd.editor=y.editor;y.wmd.previewManager=y.previewManager};a.startEditor=function(){if(y.wmd_env.autostart===false){a.makeAPI();return}var G;var F;var H=function(){y.panels=new y.PanelCollection();F=new y.previewManager();var I=F.refresh;G=new y.editor(I);F.refresh(true)};a.addEvent(top,"load",H)};y.previewManager=function(){var H=this;var V;var F;var N;var M;var S;var O;var I=3000;var P="delayed";var K=function(X,Y){a.addEvent(X,"input",Y);X.onpaste=Y;X.ondrop=Y;a.addEvent(X,"keypress",Y);a.addEvent(X,"keydown",Y);F=new y.inputPoller(Y,A)};var R=function(){var X=0;if(top.innerHeight){X=top.pageYOffset}else{if(E.documentElement&&E.documentElement.scrollTop){X=E.documentElement.scrollTop}else{if(E.body){X=E.body.scrollTop}}}return X};var L=function(){if(!y.panels.preview&&!y.panels.output){return}var Z=y.panels.input.value;if(Z&&Z==S){return}else{S=Z}var Y=new Date().getTime();if(!V&&y.showdown){V=new y.showdown.converter()}if(V){Z=V.makeHtml(Z)}var X=new Date().getTime();M=X-Y;G(Z);O=Z};var U=function(){if(N){top.clearTimeout(N);N=undefined}if(P!=="manual"){var X=0;if(P==="delayed"){X=M}if(X>I){X=I}N=top.setTimeout(L,X)}};var J=function(X){if(X.scrollHeight<=X.clientHeight){return 1}return X.scrollTop/(X.scrollHeight-X.clientHeight)};var W=function(){if(y.panels.preview){y.panels.preview.scrollTop=(y.panels.preview.scrollHeight-y.panels.preview.clientHeight)*J(y.panels.preview)}if(y.panels.output){y.panels.output.scrollTop=(y.panels.output.scrollHeight-y.panels.output.clientHeight)*J(y.panels.output)}};this.refresh=function(X){if(X){S="";L()}else{U()}};this.processingTime=function(){return M};this.output=function(){return O};this.setUpdateMode=function(X){P=X;H.refresh()};var Q=true;var G=function(aa){var X=C.getTop(y.panels.input)-R();if(y.panels.output){if(y.panels.output.value!==undefined){y.panels.output.value=aa;y.panels.output.readOnly=true}else{var Z=aa.replace(/&/g,"&amp;");Z=Z.replace(/</g,"&lt;");y.panels.output.innerHTML="<pre><code>"+Z+"</code></pre>"}}if(y.panels.preview){y.panels.preview.innerHTML=aa}W();if(Q){Q=false;return}var Y=C.getTop(y.panels.input)-R();if(v.isIE){top.setTimeout(function(){top.scrollBy(0,Y-X)},0)}else{top.scrollBy(0,Y-X)}};var T=function(){K(y.panels.input,U);L();if(y.panels.preview){y.panels.preview.scrollTop=0}if(y.panels.output){y.panels.output.scrollTop=0}};this.destroy=function(){if(F){F.destroy()}};T()};h.doAutoindent=function(F,G){F.before=F.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/,"\n\n");F.before=F.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/,"\n\n");F.before=F.before.replace(/(\n|^)[ \t]+\n$/,"\n\n");if(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(F.before)){if(h.doList){h.doList(F)}}if(/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(F.before)){if(h.doBlockquote){h.doBlockquote(F)}}if(/(\n|^)(\t|[ ]{4,}).*\n$/.test(F.before)){if(h.doCode){h.doCode(F)}}};h.doBlockquote=function(F,G){F.selection=F.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,function(L,K,J,I){F.before+=K;F.after=I+F.after;return J});F.before=F.before.replace(/(>[ \t]*)$/,function(J,I){F.selection=I+F.selection;return""});F.selection=F.selection.replace(/^(\s|>)+$/,"");F.selection=F.selection||"Blockquote";if(F.before){F.before=F.before.replace(/\n?$/,"\n")}if(F.after){F.after=F.after.replace(/^\n?/,"\n")}F.before=F.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/,function(I){F.startTag=I;return""});F.after=F.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,function(I){F.endTag=I;return""});var H=function(J){var I=J?"> ":"";if(F.startTag){F.startTag=F.startTag.replace(/\n((>|\s)*)\n$/,function(L,K){return"\n"+K.replace(/^[ ]{0,3}>?[ \t]*$/gm,I)+"\n"})}if(F.endTag){F.endTag=F.endTag.replace(/^\n((>|\s)*)\n/,function(L,K){return"\n"+K.replace(/^[ ]{0,3}>?[ \t]*$/gm,I)+"\n"})}};if(/^(?![ ]{0,3}>)/m.test(F.selection)){h.wrap(F,y.wmd_env.lineLength-2);F.selection=F.selection.replace(/^/gm,"> ");H(true);F.skipLines()}else{F.selection=F.selection.replace(/^[ ]{0,3}> ?/gm,"");h.unwrap(F);H(false);if(!/^(\n|^)[ ]{0,3}>/.test(F.selection)&&F.startTag){F.startTag=F.startTag.replace(/\n{0,2}$/,"\n\n")}if(!/(\n|^)[ ]{0,3}>.*$/.test(F.selection)&&F.endTag){F.endTag=F.endTag.replace(/^\n{0,2}/,"\n\n")}}if(!/\n/.test(F.selection)){F.selection=F.selection.replace(/^(> *)/,function(I,J){F.startTag+=J;return""})}};h.doCode=function(F,G){var I=/\S[ ]*$/.test(F.before);var K=/^[ ]*\S/.test(F.after);if((!K&&!I)||/\n/.test(F.selection)){F.before=F.before.replace(/[ ]{4}$/,function(L){F.selection=L+F.selection;return""});var J=1;var H=1;if(/\n(\t|[ ]{4,}).*\n$/.test(F.before)){J=0}if(/^\n(\t|[ ]{4,})/.test(F.after)){H=0}F.skipLines(J,H);if(!F.selection){F.startTag=" ";F.selection="enter code here"}else{if(/^[ ]{0,3}\S/m.test(F.selection)){F.selection=F.selection.replace(/^/gm," ")}else{F.selection=F.selection.replace(/^[ ]{4}/gm,"")}}}else{F.trimWhitespace();F.findTags(/`/,/`/);if(!F.startTag&&!F.endTag){F.startTag=F.endTag="`";if(!F.selection){F.selection="enter code here"}}else{if(F.endTag&&!F.startTag){F.before+=F.endTag;F.endTag=""}else{F.startTag=F.endTag=""}}}};h.doList=function(Q,J,I){var S=/(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;var R=/^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/;var F="-";var N=1;var L=function(){var T;if(I){T=" "+N+". ";N++}else{T=" "+F+" "}return T};var M=function(T){if(I===undefined){I=/^\s*\d/.test(T)}T=T.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,function(U){return L()});return T};Q.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/,null);if(Q.before&&!/\n$/.test(Q.before)&&!/^\n/.test(Q.startTag)){Q.before+=Q.startTag;Q.startTag=""}if(Q.startTag){var H=/\d+[.]/.test(Q.startTag);Q.startTag="";Q.selection=Q.selection.replace(/\n[ ]{4}/g,"\n");h.unwrap(Q);Q.skipLines();if(H){Q.after=Q.after.replace(R,M)}if(I==H){return}}var K=1;Q.before=Q.before.replace(S,function(T){if(/^\s*([*+-])/.test(T)){F=s.$1}K=/[^\n]\n\n[^\n]/.test(T)?1:0;return M(T)});if(!Q.selection){Q.selection="List item"}var O=L();var G=1;Q.after=Q.after.replace(R,function(T){G=/[^\n]\n\n[^\n]/.test(T)?1:0;return M(T)});Q.trimWhitespace(true);Q.skipLines(K,G,true);Q.startTag=O;var P=O.replace(/./g," ");h.wrap(Q,y.wmd_env.lineLength-P.length);Q.selection=Q.selection.replace(/\n/g,"\n"+P)};h.doHeading=function(H,I){H.selection=H.selection.replace(/\s+/g," ");H.selection=H.selection.replace(/(^\s+|\s+$)/g,"");if(!H.selection){H.startTag="## ";H.selection="Heading";H.endTag=" ##";return}var J=0;H.findTags(/#+[ ]*/,/[ ]*#+/);if(/#+/.test(H.startTag)){J=s.lastMatch.length}H.startTag=H.endTag="";H.findTags(null,/\s?(-+|=+)/);if(/=+/.test(H.endTag)){J=1}if(/-+/.test(H.endTag)){J=2}H.startTag=H.endTag="";H.skipLines(1,1);var K=J==0?2:J-1;if(K>0){var G=K>=2?"-":"=";var F=H.selection.length;if(F>y.wmd_env.lineLength){F=y.wmd_env.lineLength}H.endTag="\n";while(F--){H.endTag+=G}}};h.doHorizontalRule=function(F,G){F.startTag="----------\n";F.selection="";F.skipLines(2,1,true)}};Attacklab.wmd_env={};Attacklab.account_options={};Attacklab.wmd_defaults={version:1,output:"Markdown",lineLength:40,delayLoad:false};if(!Attacklab.wmd){Attacklab.wmd=function(){Attacklab.loadEnv=function(){var b=function(d){if(!d){return}for(var c in d){Attacklab.wmd_env[c]=d[c]}};b(Attacklab.wmd_defaults);b(Attacklab.account_options);b(top.wmd_options);Attacklab.full=true;var a="bold italic link blockquote code image ol ul heading hr";Attacklab.wmd_env.buttons=Attacklab.wmd_env.buttons||a};Attacklab.loadEnv()};Attacklab.wmd();Attacklab.wmdBase();Attacklab.Util.startEditor()}; \ No newline at end of file
diff --git a/templates/content/style/style.css b/templates/content/style/style.css
deleted file mode 100644
index aba67eee..00000000
--- a/templates/content/style/style.css
+++ /dev/null
@@ -1,1466 +0,0 @@
-@import url(jquery.autocomplete.css);
-@import url(openid.css);
-@import url(prettify.css);
-/* 公用 */
-body{background:#FFF; font-size:12px; line-height:150%; margin:0; padding:0; color:#000; font-family: sans-serif;
-}
-div{margin:0 auto; padding:0;}
-h1,h2,h3,h4,h5,h6,ul,li,dl,dt,dd,form,img,p{margin:0; padding:0; border:none; }
-label {vertical-align:middle;}
-hr {border:none;border-top: 1px dashed #ccccce;}
-input, select {vertical-align:middle;font-family:Trebuchet MS,"segoe ui",Helvetica,"Microsoft YaHei",宋体,Tahoma,Verdana,MingLiu,PMingLiu,Arial,sans-serif;}
-p{margin-bottom:13px; font-size:13px; line-height:140%;}
-a {color:#333333; text-decoration:none;}
-.badges a {color:#763333;text-decoration:underline;}
-a:hover {text-decoration:underline;}
-.block{width:960px; height:auto;}
-.fleft{float:left;}
-.fright{float:right;}
-.tleft{text-align:left;}
-.tcenter{text-align:center;}
-.tright{text-align:right;}
-.dis{display:block;}
-.inline{display:inline;}
-.none{display:none;}
-.red{color:#CC0000;}
-.b{font-weight:bold;}
-.f10{font-size:10px;}
-.f11{font-size:11px;}
-.f12{font-size:12px;}
-.f13{font-size:13px;}
-.f14{font-size:14px;}
-.white{color:#FFFFFF;}
-.u{text-decoration:underline;}
-.spacer1{height:6px; line-height:6px; clear:both; visibility:hidden;}
-.spacer3{height:30px; line-height:30px; clear:both; visibility:hidden;}
-h1{font-size:160%;padding:5px 0 5px 0;}
-h2{font-size:140%;padding:3px 0 3px 0;}
-h3{font-size:120%;padding:3px 0 3px 0;}
-ul
-{
- list-style: disc;
- margin-left: 20px;
- padding-left:0px;
- margin-bottom: 1em;
-}
-ol
-{
- list-style: decimal;
- margin-left: 30px;
- margin-bottom: 1em;
- padding-left:0px;
-}
-td ul {
- vertical-align:middle;
-}
-li input {
- margin: 3px 3px 4px 3px;
-}
-pre
-{
- font-family: Consolas, Monaco, Liberation Mono, Lucida Console, Monospace;
- font-size:100%;
- margin-bottom: 10px;
- overflow: auto;
- width: 580px;
- background-color: #F5F5F5;
- padding-left:5px;
- padding-top:5px;
- padding-bottom: 20px !ie7;
-}
-
-code{
- font-family: Consolas, Monaco, Liberation Mono, Lucida Console, Monospace;
- font-size:100%;
-
-}
-
-blockquote
-{
- margin-bottom: 10px;
- margin-right: 15px;
- padding: 10px 0px 1px 10px;
- background-color: #F5F5F5;
-}
-
-/*页面布局*/
-#wrapper {width:960px;margin:auto;padding:0;}
-#roof {
- position:relative;
- margin-top:0px;
- background:#FFF;
-}
-#room {padding:10px 0 10px 0;background-color:#FFF;border-bottom:1px solid #777;}
-#CALeft{width:710px; float:left; position:relative;}
-#CARight{width:240px; float:right;}
-#CAFull{float:left;padding:0 5px 0 5px;width:950px;}
-#ground {width:100%;border-top:1px solid #000; padding-top:6px; padding-bottom:0px; text-align:center;background:#777;}
-/*#licenseLogo {position:absolute;top:10px;right:10px;}*/
-
-/*顶部及导航栏*/
-#top {
- position:absolute;
- top:0px;
- right:0px;
- height:20px;
- text-align:right;
- padding:3px;
- background-color:#ffffff;
- width:500px;
-}
-/*#header {width:960px;}*/
-#top a {height:35px; text-align:right;
- /*letter-spacing:1px; */
- margin-left:20px;text-decoration:underline; font-size:12px; color:#333333;}
-#logo {
- padding: 5px 0px 0px 0px;
-}
-#navBar {float:clear;position:relative;display:block;width:960px;}
-#navBar .nav {margin:20px 0px 0px 16px;
- /*letter-spacing:1px; */
- }
-#navBar .nav a {color:#333333; background-color:#fff0e0;
- /*border-left: 1px solid #eeeeec;
- border-right: 1px solid #babdb6;
- border-top: 1px solid #eeeeec;*/
- border: 1px solid #888888;
- border-bottom: none;
- padding:0px 12px 3px 12px; height:25px; line-height:30px;margin-left:10px; font-size:14px; font-weight:400; text-decoration:none;display: block;float: left;}
-#navBar .nav a:hover {text-decoration:underline}
-#navBar .nav a.on {height:24px;line-height:28px;
- border-bottom: 1px solid #a40000;
- border-right:1px solid #820000;
- border-top:1px solid #d40000;
- border-left:1px solid #d40000;
- /*background:#A31E39; */
- background:#a40000;
- color:#FFF; font-weight:600; text-decoration:none}
-#navBar .nav a.special {font-size:14px; color:#B02B2C; font-weight:bold; text-decoration:none; }
-#navBar .nav a.special:hover {text-decoration:underline;}
-#navBar .nav div.focus {float:right; padding-right:0px;}
-/*搜索栏*/
-#searchBar {width:958px;
- background-color:#888a85;/*#e9b96e;*/
- border: 1px solid #aaaaaa;
- padding:4px 0 0 0;} /* #B02B2C */
-#searchBar .content { }
-#searchBar .searchInput {font-size:13px; height:18px; width:400px;}
-#searchBar .searchBtn {font-size:14px; height:26px; width:80px;}
-#searchBar .options {padding:3px 0 3px 0;font-size:100%;color:#EEE;
- /*letter-spacing:1px;*/
- }
-#searchBar .options INPUT {margin:0 3px 0 15px;}
-#searchBar .options INPUT:hover {cursor:pointer}
-
-/*问题列表*/
-#listA {float:left; background-color:#FFF;padding:0 0px 0 0px; width:100%;}
-#listA .qstA {
- position:relative;
- padding:3px 5px 5px 10px;
- border-top:1px dashed #ccccce;
- /*border-left:1px solid #ebebbe;
- border-right:1px solid #b4b48e;
- border-bottom:1px solid #b4b48e;*/
- background: white;/* #f9f7ed;*/
- /*margin:10px 0 10px 0;*/
- /*background:url(../images/quest-bg.gif) repeat-x top;*/
-}
-#listA .qstA thumb {float:left; }
-#listA .qstA H2 {font-size:14px; font-weight:800; margin:8px auto;padding:0px;}
-#listA .qstA H2 a {color:333333/*#2e3436*/;font-size:15px;}
-#listA .qstA .stat {
- position:absolute;
- right:0px;
- bottom:5px;
- font-size:12px;
- /*letter-spacing:1px;*/
- float:right;
-}
-#listA .qstA .stat span {margin-right:5px;}
-#listA .qstA .stat td {min-width:40px;text-align:center;}
-#listA .qstA .stat .num {
- font-family:sans-serif;
- color:#a40000;
- /*background:#eeeeec;
- border: 1px solid #babdb6;*/
- margin:0px;
- font-size:17px;
- font-weight:800;
-}
-#listA .qstA table {border-spacing:0px;}
-#listA .qstA table td {padding:0px;width:60px;text-align:center;}
-#listA .qstA .stat .unit {color:#777777;margin:0px}
-#listA .qstA .from {margin-top:5px; font-size:13px;color:#777777;}
-#listA .qstA .from .score {font-family:sans-serif;color:#555555;}
-#listA .qstA .date {margin-left:10px; color:#777777;}
-#listA .qstA .wiki {color:#763333;font-size:12px;}
-#listA .qstA .from a {}
-#listA .qstA .from IMG {vertical-align:middle;}
-#listA .qstA .author {font-weight:400; }
-#listA .qstA .author a{color:#444444; }
-#listA .qstA .summary{margin-right:5px;}
-#question-table {
- margin-bottom:10px;
- /*border-bottom:1px solid #888a85;*/
-}
-.evenMore {font-size:14px; font-weight:800;}
-.questions-count{
- font-size:32px;
- font-family:sans-serif;
- font-weight:600;
- padding:0 0 5px 0px;
- color:#a40000;
- margin-top:3px;
-}
-
-/*内容块*/
-.boxA {background:#888a85; padding:6px; margin-bottom:8px;border 1px solid #babdb6;}
-.boxA H3 {font-size:13px; font-weight:800; color:#FFF; margin:0; padding:0; margin-bottom:4px;}
-.boxA .body {border:1px solid #999; padding:8px; background:#FFF; font-size:13px;}
-.boxA .more {padding:2px; text-align:right; font-weight:800;}
-.boxB {background:#F9F7ED; padding:6px; margin-bottom:8px;border:solid 1px #aaaaaa;}
-.boxB H3 {font-size:13px; font-weight:800; color:#000; margin:0; padding:0 0 0 15px; margin-bottom:4px; background:url(../images/dot-g.gif) no-repeat left center;}
-.boxB .body {border:1px solid #aaaaaa; padding:8px; background:#FFF; font-size:13px; line-height:160%;}
-.boxB .more {padding:1px; text-align:right; font-weight:800;}
-.boxC {
- background: #cacdc6;/*f9f7ed;*/
- padding:10px;
- margin-bottom:8px;
- border-top:1px solid #eeeeec;
- border-left:1px solid #eeeeec;
- border-right:1px solid #a9aca5;
- border-bottom:1px solid #babdb6;
-}
-.boxC p {
- margin-bottom:8px;
-}
-.boxC p.nomargin {
- margin:0px;
-}
-.boxC p.info-box-follow-up-links {
- text-align:right;
- margin:0;
-}
-/*分页*/
-.pager {margin-top:10px; margin-bottom:16px; float:left;}
-.pagesize {margin-top:10px; margin-bottom:16px; float:right;}
-
-/** PAGINATOR **/
-.paginator {
- padding:5px 0 10px 0;
- font:normal 12px sans-serif;
-}
-
-.paginator .prev-na,
-.paginator .next-na {
- padding:.3em;
- font:bold .875em sans-serif;
-}
-
-.paginator .prev-na,
-.paginator .next-na {
- border:1px solid #ccc;
- background-color:#f9f9f9;
- color:#aaa;
- font-weight:normal;
-}
-
-.paginator .prev a, .paginator .prev a:visited,
-.paginator .next a, .paginator .next a:visited {
- border:1px solid #fff;
- background-color:#fff;
- color:#777;
- padding:2px 4px 3px 4px;
- font:bold 100% sans-serif;
-}
-
-.paginator .prev, .paginator .prev-na { margin-right:.5em; }
-.paginator .next, .paginator .next-na { margin-left:.5em; }
-
-.paginator .page a, .paginator .page a:visited, .paginator .curr {
- padding:.25em;
- font:normal .875em verdana;
- border:1px solid #ccc;
- background-color:#fff;
- margin:0em .25em;
- color:#777;
-}
-
-.paginator .curr {
- background-color:#777;
- color:#fff;
- border:1px solid #777;
- font-weight:bold;
-}
-
-.paginator .page a:hover,
-.paginator .curr a:hover,
-.paginator .prev a:hover,
-.paginator .next a:hover {
- color:#fff;
- background-color:#777;
- border:1px solid #777;
- text-decoration:none;
-}
-
-.paginator .text{
- color:#777;
- padding:.3em;
- font:bold 100% sans-serif;
-}
-
-.paginator-container{
- float:right;
- padding:10px 0 10px 0;
-}
-
-.paginator-container-left{
- padding:5px 0 10px 0;
-}
-
-/*标签*/
-.tag {font-size:13px; font-weight:normal; color:#333; text-decoration:none;background-color:#EEE; border-left:3px solid #777; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:1px 8px 1px 8px;}
-.tags {font-family:sans-serif; line-height:200%; display:block; margin-top:5px;}
-.tags a {white-space: nowrap; font-size:13px; font-weight:normal; color:#333; text-decoration:none;background-color:#EEE; border-left:3px solid #777; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:1px 8px 1px 8px;}
-.tags a:hover {background-color:#fFF;color:#333;}
-.tagsbox {line-height:200%;}
-.tagsbox a {font-size:13px; font-weight:normal; color:#333; text-decoration:none;background-color:#EEE; border-left:3px solid #777; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:1px 8px 1px 8px;}
-.tagsbox a:hover {background-color:#fFF;color:#333;}
-.tag-number {font-weight:700;font-family:sans-serif;}
-.marked-tags { margin-top: 0px;margin-bottom: 5px; }
-.deletable-tag { margin-right: 3px; white-space:nowrap; }
-
-/*奖牌*/
-a.medal { font-size:14px; line-height:250%; font-weight:800; color:#333; text-decoration:none; background:url(../images/medala.gif) no-repeat; border-left:1px solid #EEE; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:4px 12px 4px 6px;}
-a:hover.medal {color:#333; text-decoration:none; background:url(../images/medala_on.gif) no-repeat; border-left:1px solid #E7E296; border-top:1px solid #E7E296; border-bottom:1px solid #D1CA3D; border-right:1px solid #D1CA3D;}
-
-/*Tab栏*/
-.tabBar{background-color:#FFF;border-bottom: 1px solid white;height: 30px; width: 100%;clear:both; margin-bottom:3px;}
-.tabsA {background-color:#FFF;float:right;position:relative;display:block;font-weight:bold;height:20px;}
-.tabsB {background-color:#FFF;float:left;position:relative;display:block;font-weight:bold;height:20px;}
-.tabsA a.on, .tabsA a:hover,.tabsB a.on, .tabsB a:hover {
- background: #fff;
- color:#a40000;
- border-top:1px solid #babdb6;
- border-left:1px solid #babdb6;
- border-right:1px solid #888a85;
- border-bottom:1px solid #888a85;
- height: 24px;
- line-height: 26px;
- margin-top: 3px;
- padding: 0px 11px 0px 11px;}
-
-.tabsA a {
- background: #f9f7eb;
- border-top:1px solid #eeeeec;
- border-left:1px solid #eeeeec;
- border-right:1px solid #a9aca5;
- border-bottom:1px solid #888a85;
- color: #888a85;
- display: block;
- float: left;
- height: 20px;
- line-height: 22px;
- margin: 5px 4px 0 0;
- padding: 0 11px 0 11px;
- text-decoration: none;
-}
-.tabsB a {background: #eee;
- border: 1px solid #eee;
- color: #777;
- display: block;
- float: left;
- height: 22px;
- line-height: 28px;
- margin: 5px 0px 0 4px;
- padding: 0 11px 0 11px;
- text-decoration: none;
-}
-/*.tabsA a:hover, .tabsB a:hover {background: #fff;border: 1px solid #777;border-bottom:3px solid #FFF;}*/
-.headlineA {font-size:13px; border-bottom:1px solid #777; padding-bottom:2px; font-weight:800; margin-bottom:12px; text-align:right; height:30px;}
-.headQuestions {float:left; height:23px; line-height:23px; margin:5px 0 0 5px;padding:0px 6px 0px 15px; font-size:15px; font-weight:700; border-bottom:0px solid #777; border-left:0px solid #darkred; background-color:#FFF;background:url(../images/dot-list.gif) no-repeat left center;}
-.headAnswers {float:left; padding:3px; font-size:18px; font-weight:800; background:url(../images/ico_answers.gif) left 2px no-repeat; padding-left:24px;}
-.headTags {float:left; padding:3px; font-size:18px; font-weight:800; background:url(../images/ico_tags.gif) no-repeat; padding-left:24px;}
-.headUsers {float:left; height:23px; line-height:23px; margin:5px 0 0 5px;padding:0px 6px 0px 15px; font-size:15px; font-weight:700; border-bottom:0px solid #777; border-left:0px solid #darkred; background-color:#FFF;background:url(../images/dot-list.gif) no-repeat left center;}
-.headMedals {float:left; height:23px; line-height:23px; margin:5px 0 0 5px;padding:0px 6px 0px 15px; font-size:15px; font-weight:700; border-bottom:0px solid #777; border-left:0px solid #darkred; background-color:#FFF;background:url(../images/dot-list.gif) no-repeat left center;}
-.headLogin {float:left; padding:3px; font-size:15px; font-weight:800; background:url(../images/ico_login.gif) no-repeat; padding-left:24px;}
-.headNormal {
- text-align:left;
- padding:3px;
- font-size:15px;
- margin-bottom:12px;
- font-weight:bold;
- border-bottom: 1px solid #777;
-}
-.headUser {text-align:left;padding:5px; font-size:20px;
- /*letter-spacing:1px;*/
- margin-bottom:12px; font-weight:800;border-bottom:1px solid #777;}
-/*RSS订阅*/
-#feeds {margin:10px 0; }
-#feeds a {background:url(../images/feed-icon-small.png) no-repeat 0; padding-left:18px; font-weight:700; font-size:13px; }
-
-/*问题*/
-#question {margin-bottom:30px;}
-#question h1{font-size:15px;background:#CCC; padding:6px 8px;;}
-#question .body{background:#F7F7F7; padding:20px 10px;}
-.starter {padding:10px; background:#E0EAF1;}
-.vote {font-size:20px; color:#666; font-weight:800;}
-.questions-related{font-weight:700;word-wrap:break-word;}
-.questions-related p{line-height:20px; margin-bottom:10px;font-size:100%;}
-.question-status{
- margin-top:10px;
- padding: 20px;
- background-color:#F5F5F5;
- text-align:center;
-}
-.question-status h3{font-size:125%;}
-.question-body{
- min-height:100px;
- font-size:13px;
- line-height:20px;
-}
-.question-body IMG{
- max-width:600px;
-}
-.question-mark{
- /*background-color:#fff5e0;
- border-top: 1px solid #eeeeec;
- border-right: 1px solid #babdb6;
- border-bottom: 1px solid #babdb6;
- border-left: 1px solid #eeeeec;*/
- text-align:left;
- padding:5px;
- overflow:hidden;
-}
-.question-edit{
- text-align:left;
- overflow:hidden;
-}
-.vote-buttons {float:left;text-align:center;}
-.vote-buttons IMG{cursor:pointer;}
-.vote-number{
- font-family:Arial;
- padding:0px 0 3px 0;
- font-size:140%;
- font-weight:bold;
- color:#777;
-}
-.question-img-upvote:hover{background:url(../images/vote-arrow-up-on.png)}
-.question-img-downvote:hover{background:url(../images/vote-arrow-down-on.png)}
-.question-img-favorite:hover{background:url(../images/vote-favorite-on.png)}
-.favorite-number{padding:0px;font-size:100%; font-family:Arial;font-weight:bold;color:#777;}
-.vote-notification
-{
- z-index: 1;
- cursor: pointer;
- display: none;
- position: absolute;
- padding: 15px;
- color: White;
- background-color: darkred;
- text-align: center;
-}
-.vote-notification a
-{
- color: White;
- text-decoration:underline;
-}
-.offensive-flag a{
- color:#777;
- padding:3px;
- cursor:pointer;
-}
-
-.offensive-flag a:hover{
- background-color:#777;
- text-decoration:none;
- color:#fff;
-}
-
-.linksopt a{
- color:#777;
- padding:3px;
- cursor:pointer;
-}
-
-.linksopt a:hover{
- background-color:#777;
- text-decoration:none;
- color:#fff;
-}
-
-.action-link a{
- color:#777;
- padding:3px;
- cursor:pointer;
-}
-
-.action-link: a hover{
- background-color:#777;
- text-decoration:none;
- color:#fff;
-}
-.action-link-separator{
- color:#ccc;
-}
-.wiki-category{
- margin-left:5px;
- color:#999;
- font-size:90%;
-}
-
-div.comments {
- line-height:150%;
- padding:10px 0;
-}
-
-div.post-comments{
- clear:both;
- background: url(../images/gray-up-arrow-h18px.png) no-repeat;
- width:100%;
- padding-left: 12px;
- margin:3px 0 10px 0;
-}
-
-form.post-comments textarea {
- height:6em;
- margin-bottom:4px;
-}
-
-form.post-comments input {
- margin-left:10px;
- margin-top:1px;
- vertical-align:top;
- width:100px;
-}
-span.text-counter {
- margin-right:20px;
- font-size:11px;
-}
-
-span.form-error {
- color:#990000;
- font-weight:normal;
- margin-left:5px;
-}
-p.form-item {
- margin:0px;
-}
-
-div.comments-container, div.comments-container-accepted, div.comments-container-owner, div.comments-container-deleted {
- padding:0;
-}
-
-.post-comments a {
- color:#888888;
- padding:0 3px 2px;
-}
-
-a.comments-link, a.comments-link-accepted, a.comments-link-owner, a.comments-link-deleted {
- color:black;
- font-size:11px;
- background: #eeeeee;
- padding:3px;
- cursor:pointer;
-}
-
-.post-comments a:hover {
- background-color:#777777;
- color:white;
- text-decoration:none;
-}
-
-a.comment-user, a.comment-user:hover {
- background-color:inherit;
- color:blue;
- padding:0;
-}
-
-a.comment-user:hover {
- text-decoration:underline;
-}
-/*回答*/
-#answers {}
-.answer{
- padding-top:10px;
- width: 100%;
- border-bottom:1px solid #ccccce;
-}
-.answer-body{
- min-height:80px;
- font-size:13px;
- line-height:20px;
-}
-
-.answer-body IMG{
- max-width:600px;
-}
-
-.accepted-answer{
- background-color:#EBFFE6;
- border-bottom-color:#9BD59B;
-}
-
-.accepted-answer .comments-link{
- background-color:#CCFFBF;
-}
-
-.accepted-answer .comments-container{
- background-color:#CCFFBF;
-}
-
-.answered
-{
- background: #CCC;
- color: #999;
-}
-
-.answered-accepted
-{
- background: #CCC;
- color: #763333;
-}
-
-.unanswered
-{
- background: #777;
- color: white;
-}
-
-.answered-by-owner
-{
- background: #E9E9FF;
-}
-
-.answered-by-owner .comments-link
-{
- background-color:#E6ECFF;
-}
-
-.answered-by-owner .comments-container
-{
- background-color:#E6ECFF;
-}
-
-.answered-accepted strong
-{
- color: #E1E818;
-}
-
-.answer-img-accept:hover{background:url(../images/vote-accepted-on.png)}
-
-.deleted{
- background:#F4E7E7 none repeat scroll 0 0;
-}
-
-/*标签列表*/
-/*
-.tagsbox {}
-.tagsbox a {color:#000;line-height:30px;margin-right:10px;font-size:100%;background-color:#F9F7ED;padding:3px;border:1px solid #aaaaaa;}
-.tagsbox a:hover {text-decoration:none;background-color:#F9F7ED;color:#B02B2C;} */
-.tagsList {margin:0; list-style-type:none;padding:0px;min-height:360px;}
-.tagsList li {width:235px; float:left;}
-.badge-list{margin:0; list-style-type:none;}
-/*登录*/
-.list-item{margin-left:15px;}
-.list-item LI{list-style-type:disc; font-size:13px; line-height:20px; margin-bottom:10px;}
-/* openid styles */
-.form-row{line-height:25px;}
-table.form-as-table {
- margin-top:5px;
-}
-table.form-as-table ul {
- list-style-type:none;
- display: inline;
-}
-table.form-as-table li {
- display: inline;
-}
-table.form-as-table td {
- text-align:right;
-}
-table.form-as-table th {
- text-align:left;
- font-weight:normal;
-}
-/*.form-row li label {
- display: inline
-}*/
-.submit-row{
- line-height:30px;
- padding-top:10px;
- display: block;
- clear: both;
-}
-.errors{line-height:20px;color:red;}
-.error{
- color:darkred;
- margin:0;
- font-size: 10px;
-}
-.error-list li{padding:5px;}
-.fieldset{
-/* border:solid 1px #777;*/
- border: none;
- margin-top:10px;
- padding:10px;
-}
-.openid-input{background:url(../images/openid.gif) no-repeat;padding-left:15px;cursor:pointer;}
-.openid-login-input{
- background-position:center left;
- background:url(../images/openid.gif) no-repeat 0% 50%;
- padding:5px 5px 5px 15px;
- cursor:pointer;
- font-family:Trebuchet MS;
- font-weight:300;
- font-size:150%;
- width:500px;
-}
-
-.openid-login-submit{
- height:40px;
- width:80px;
- line-height:40px;
- cursor:pointer;
- border:1px solid #777;
- font-weight:bold;
- font-size:120%;
-}
-
-.openid-samples{
-
-}
-
-.openid-samples .list, .list li{
- font-family:Trebuchet MS,"segoe ui",Helvetica,"Microsoft YaHei",宋体,Tahoma,Verdana,MingLiu,PMingLiu,Arial,sans-serif;
- list-style:none !important;
- margin-left:-30px !important;
- line-height:20px !important;
-}
-
-/*表单相关*/
-span.form-error {
- color:#990000;
- font-size:90%;
- font-weight:normal;
- margin-left:5px;
-}
-.title-desc{
- color:#666666;
- font-size:90%;
-}
-
-/*adjustment for editor preview*/
-#editor{
- font-size:100%;
- min-height:200px;
- line-height: 18px;
- width:100%;
-}
-
-.wmd-preview{
- margin-top:10px;
- padding:6px;
- width:100%;
- background-color:#F5F5F5;
- min-height:20px;
-}
-.wmd-preview pre{
- background-color:#E7F1F8;
-
-}
-
-.wmd-preview blockquote
-{
- background-color: #eee;
-}
-
-.wmd-preview IMG{
- max-width:600px;
-}
-.preview-toggle{
- font-weight:600;
- width:100%;
- color:#aaa;
- /*letter-spacing:1px;*/
- text-align:left;
-}
-
-.preview-toggle span:hover{
- cursor:pointer;
-}
-
-.edit-content-html{
- border-top:1px dotted #D8D2A9;
- border-bottom:1px dotted #D8D2A9;
- margin:5px 0 5px 0;
-}
-
-/*修订记录*/
-
-#revisions{
- width:950px;
-}
-
-.revision{
- margin:10px 0 10px 0;
- width:100%;
- font-size:13px;
-}
-
-.revision .header{
- background-color:#eee;
- padding:5px;
- cursor:pointer;
-}
-
-.revision .author{
- background-color:#E9E9FF;
-}
-
-.revision .summary{
- padding: 5px 0 10px 0;
-}
-
-.revision .summary span{
- background-color:yellow;
- padding-left:3px;
- padding-right:3px;
- display:inline;
-}
-.revision h1{
- font-size:130%;
- font-weight:600;
- padding:15px 0 15px 0;
-}
-
-.revision-mark{
- width:200px;
- text-align:left;
- display:inline-block;
- font-size:90%;
- overflow:hidden;
-}
-
-.revision-number{
- font-size:300%;
- font-weight:bold;
- font-family:sans-serif;
-}
-
-.revision .body{
- padding-left:10px;
- margin-bottom:50px;
-}
-.revision .answerbody{
- padding:10px 0 5px 10px;
-}
-
-/* Revision pages */
-del { color: #FF5F5F; }
-del .post-tag{
-color: #FF5F5F;
-}
-ins { background-color: #97ff97;}
-ins .post-tag{
-background-color: #97ff97;
-}
-
-/*用户资料页面*/
-.count {font-family:Arial;font-size:200%;font-weight:700;color:#777}
-.scoreNumber{font-family:Arial;font-size:35px;font-weight:800;color:#777;line-height:40px;
- /*letter-spacing:0px*/
- }
-.user-details{font-size:13px;}
-.user-about{background-color:#EEEEEE;height:200px;line-height:20px; overflow:auto;padding:10px;width:90%;}
-.user-edit-link {background:url(../images/edit.png) no-repeat; padding-left:20px;}
-.favorites-count-off {
- color:#919191;
- float:left;
- padding:3px;
- margin:10px 0 0 0 ;
- text-align:center;
-}
-
-.favorites-count {
- color:#D4A849;
- float:left;
- padding:3px;
- margin:10px 0 0 0 ;
- text-align:center;
-}
-.favorites-empty{
- width: 32px; height: 45px; float: left;
-}
-.question-summary {
- border-bottom:1px dotted #999999;
- float:left;
- overflow:hidden;
- padding:11px 0;
- width:670px;
-}
-
-.user-info-table{
-width:950;margin-bottom:10px;
-}
-
-.user-stats-table .question-summary {
- width:800px;
-}
-
-.narrow .stats {
- background:transparent none repeat scroll 0 0;
- float:left;
- height:48px;
- margin:0 0 0 7px;
- padding:0;
- width:auto;
- font-family:Arial;
-}
-
-.stats div {
- font-size:11px;
- text-align:center;
-}
-
-.narrow .votes {
- background:#EEEEEE none repeat scroll 0 0;
- float:left;
- height:42px;
- margin:0 3px 0 0;
- padding:5px;
- width:46px;
- text-align:center;
- -moz-border-radius: 5px;
- -khtml-border-radius: 5px;
- -webkit-border-radius: 5px;
-}
-
-.narrow .summary {
- width:600px;
- display:inline-block;
-}
-
-.narrow .summary h3 {
- padding:0px;
- margin:0px;
-}
-
-.narrow .views {
- height:42px;
- float:left;
- margin:0 7px 0 0;
- /*padding:5px 0 5px 4px;*/
- padding: 5px;
- width:46px;
- text-align:center;
- -moz-border-radius: 5px;
- -khtml-border-radius: 5px;
- -webkit-border-radius: 5px;
- color:#777;
-}
-
-.narrow .status {
- float:left;
- height:42px;
- margin:0 3px 0 0;
- padding:5px;
- width:46px;
- text-align:center;
- -moz-border-radius: 5px;
- -khtml-border-radius: 5px;
- -webkit-border-radius: 5px;
-}
-
-.narrow .vote-count-post {
- font-weight:800;
- display:block;
- margin:0;
- font-size: 190%; color:#555; line-height:20px;
-}
-.narrow .answer-count-post{
- font-weight:800;
- display:block;
- margin:0;
- font-size: 190%;
-}
-.narrow .views-count-post{
- font-weight:800;
- display:block;
- margin:0;
- font-size: 190%;
-}
-div.started {
- color:#999999;
- float:right;
- line-height:18px;
-
-}
-
-.narrow div.started {
- line-height:inherit;
- padding-top:4px;
- white-space:nowrap;
- width:auto;
-}
-
-.relativetime {
- font-weight:bold;
- text-decoration:none;
-}
-
-div.started a {
- font-weight:bold;
-}
-
-div.started .reputation-score {
- margin-left:1px;
-}
-
-.narrow .tags{float:left;}
-
-.answer-summary {
- display:block;
- clear:both;
- padding:3px;
-}
-
-.answer-votes {
- background-color:#EEEEEE;
- color:#555555;
- float:left;
- font-family:Arial;
- font-size:110%;
- font-weight:bold;
- height:15px;
- padding:4px 4px 5px;
- text-align:center;
- text-decoration:none;
- width:20px;
- margin-right:10px;
-}
-.vote-count{font-family:Arial; font-size:160%; font-weight:700; color:#777;}
-.user-action{
-
-}
-.user-action-1{
- font-weight:bold;
- color:#333;
-}
-.user-action-2{
- font-weight:bold;
- color:#CCC;
-}
-.user-action-3{
- color:#333;
-}
-.user-action-4{
- color:#333;
-}
-.user-action-5{
- color:darkred;
-}
-.user-action-6{
- color:darkred;
-}
-.user-action-7{
- color:#333;
-}
-.user-action-8{
- padding:3px;
- font-weight:bold;
- background-color:#CCC;
- color:#763333;
-}
-
-.revision-summary{
- background-color:#FFFE9B;
- padding:2px;
-}
-.question-title-link a{
- font-weight:bold;
- color:#0077CC;
-}
-.answer-title-link a{
- color:#333;
-}
-
-.post-type-1 a {
- font-weight:bold;
-
-}
-.post-type-3 a {
- font-weight:bold;
-
-}
-.post-type-5 a {
- font-weight:bold;
- }
-.post-type-2 a{
- color:#333;
-}
-.post-type-4 a{
- color:#333;
-}
-.post-type-6 a{
- color:#333;
-}
-.post-type-8 a{
- color:#333;
-}
-
-
-/*读书频道*/
-.bookInfo {float:left; width:940px;padding:5px;}
-.bookCover {float:left; width:200px;}
-.bookCover img{border:1px solid #ccc;max-width:200px;}
-.bookSummary {float:left; font-size:13px;}
-.blogRss {float:right;margin:0 10px 0 0;width:460px;height:240px;background-color:#EEE; padding:5px;}
-.bookQuestions {margin-bottom:10px;}
-.bookFeed {float:right;}
-.bookAsk{
- /*letter-spacing:1px; */
- float:right;margin:-30px 10px 0 0; padding:3px 5px 3px 5px;}
-.bookAsk a {font-size:15px; color:#FFF; font-weight:bold; text-decoration:none;background-color:#EC7000;padding:3px 6px 3px 6px; }
-.bookAsk a:hover {text-decoration:underline;}
-
-
-/*其他全局样式*/
-.hilite { background-color: #ff0; }
-.hilite1 { background-color: #ff0; }
-.hilite2 { background-color: #f0f; }
-.hilite3 { background-color: #0ff; }
-.userStatus {margin-left:12px; color:#FFF; float:right;}
-.userStatus a {color:#FFF;}
-.gold, .badge1 {color:#FFCC00;}
-.silver, .badge2 {color:#CCCCCC;}
-.bronze, .badge3 {color:#CC9933;}
-.score {font-weight:800; color:#333;}
-.footerLinks {color:#EEE; font-size:13px;
- /* letter-spacing:1px;*/
- }
-.footerLinks a {color:#FFF; font-size:13px;}
-.subSearch {margin-bottom:12px; padding:4px;}
-a.comment {background:#EEE; color:#993300; padding:4px;}
-a.permLink {padding:2px;}
-a.offensive {color:#999;}
-ul.bulleta li {background:url(../images/bullet_green.gif) no-repeat 0px 2px; padding-left:16px; margin-bottom:4px;}
-.user {padding:5px; line-height:140%; width:170px;}
-.user ul {margin:0; list-style-type:none;}
-.user .thumb{clear:both;float:left; margin-right:4px; display:inline;}
-.yellowbg{background:yellow;}
-
-.message{
- padding:5px;
- margin:10px 0 10px 0;
- background-color:#eee;
- border: 1px solid #aaaaaa;
-}
-.message h1 {
- padding-top:0px;
- font-size:15px;
-}
-.message p {
- margin-bottom:0px;
-}
-p.space-above {
- margin-top:10px;
-}
-
-.warning{color:red;}
-.darkred{color:darkred;}
-.submit{
- cursor:pointer;
- /*letter-spacing:1px;*/
- background-color:#D4D0C8;
- height:40px;
- border:1px solid #777777;
-/* width:100px; */
- font-weight:bold;
- padding-bottom:4px;
- font-size:120%;}
-.submit:hover{text-decoration:underline;}
-.ask-body{padding-right:10px;}
-.thousand{color:orange;}
-
-.notify
-{
- position: fixed;
- top: 0px;
- left: 0px;
- width: 100%;
- z-index: 100;
- padding: 0;
- text-align: center;
- font-weight: Bold;
- color: #444;
- background-color: #F4A83D;
-}
-
-.notify p {
- margin-top:5px;
- margin-bottom:5px;
- font-size:16px;
-}
-
-#close-notify
-{
- position:absolute;
- right:5px;
- top:5px;
- padding:0 3px 0 3px;
- color: #735005;
- text-decoration: none;
- font-size:14px;
- line-height:18px;
- background-color: #FAD163;
- border: 2px #735005 solid;
- cursor:pointer;
-}
-#close-notify:hover {
- text-decoration:none;
-}
-
-.big {
- font-size:15px;
-}
-.bigger {
- font-size:14px;
-}
-.strong {
- font-weight:bold;
-}
-.orange
-{
- color:#d64000;
- font-weight:bold;
-}
-.grey {
- color:#808080;
-}
-.about div {
- padding:10px 5px 10px 5px;
- border-top:1px dashed #aaaaaa;
-}
-.about div.first {
- padding-top:0;
- border-top:none;
-}
-.about p {
- margin-bottom:10px;
-}
-.about a {color:#d64000;text-decoration:underline;}
-.about h3{
- line-height:30px;
- font-size:15px;
- font-weight:700;
- padding-top: 0px;
-}
-.highlight {
- background-color:#FFF8C6;
-}
-.nomargin {
- margin:0;
-}
-.margin-bottom {
- margin-bottom: 10px;
-}
-.inline-block {
- display:inline-block;
-}
-.action-status {
- margin:0;
- border:none;
- text-align:center;
- line-height:10px;
- font-size:12px;
- padding:0;
-}
-.action-status span {
- padding:3px 5px 3px 5px;
- background-color:#fff380;/* nice yellow */
- font-weight:normal;
- -moz-border-radius: 5px;
- -khtml-border-radius: 5px;
- -webkit-border-radius: 5px;
-}
-.tight {
- margin:0;
- padding:0;
-}
-
-.list-table td {
- vertical-align:top;
-}
-
-p.comment {
- border-top: 1px dotted #ccccce;
- margin:0;
- font-size:11px;
- color: #444444;
- padding:5px 0 5px 0;
-}
-
-.delete-icon {
- vertical-align:middle;
- padding-left:3px;
-}
-/* these need to go */
-table.form-as-table .errorlist {
- display: block;
- margin:0;
- padding:0 0 0 5px;
- text-align:left;
- font-size:10px;
- color:darkred;
-}
-table.form-as-table input {
- display: inline;
- margin-left: 4px;
-}
-table.form-as-table th {
- vertical-align:bottom;
- padding-bottom:4px;
-}
-.form-row-vertical {
- margin-top: 8px;
- display: block;
-}
-.form-row-vertical label {
- margin-bottom:3px;
- display:block;
-}
-/* above stuff needs to go */
-.text-align-right {
- text-align: center;
-}
-ul.form-horizontal-rows {
- list-style:none;
- margin:0;
-}
-ul.form-horizontal-rows li {
- position:relative;
- height:40px;
-}
-ul.form-horizontal-rows label {
- display:inline-block;
-}
-ul.form-horizontal-rows ul.errorlist {
- list-style:none;
- color:darkred;
- font-size:10px;
- line-height:10px;
- position:absolute;
- top:2px;
- left:180px;
- text-align:left;
- margin:0;
-}
-ul.form-horizontal-rows ul.errorlist li {
- height:10px;
-}
-ul.form-horizontal-rows label {
- position:absolute;
- left:0px;
- bottom:6px;
- margin:0px;
- line-height: 12px;
- font-size: 12px;
-}
-ul.form-horizontal-rows li input {
- position:absolute;
- bottom:0px;
- left:180px;
- margin:0px;
-}
-#emailpw-form li input {
- left:170px;
-}
-#emailpw-form ul.errorlist {
- left:170px;
-}
-#changepw-form li input {
- left:150px;
-}
-#changepw-form ul.errorlist {
- left:150px;
-}
-.narrow .summary {
- float: left;
-}
-.narrow .summary .question-title {
- font-weight: bold;
- font-size: 120%;
-}
-.user-profile-tool-links {
- padding-bottom:10px;
- font-weight: bold;
-}
-.post-controls {
- float:left;
- font-size:11px;
- line-height:12px;
- min-width:200px;
- margin-bottom:5px;
-}
-#question-controls .tags {
- margin:0 0 3px 0;
-}
-.post-update-info-container {
- float: right;
- min-width:190px;
-}
-.post-update-info {
- display:inline-block;
- float:right;
- width:190px;
- margin-bottom:5px;
-}
-.post-update-info p {
- font-size:11px;
- line-height:15px;
- margin:0 0 4px 0;
- padding:0;
-}
-.post-update-info img {
- float: left;
- width: 32px;
- margin: 4px 8px 0 0;
-}
-.comments-container {
- clear:both;
-}
-.admin {
- background-color:#fff380;/* nice yellow */
- border: 1px solid darkred;
- padding: 0 5px 0 5px;
-}
-.admin p {
- margin-bottom: 3px;
-}
-.admin #action_status {
- text-align:center;
- font-weight:bold;
-}
-#tagSelector {
- padding-bottom: 2px;
-}
-#hideIgnoredTagsControl {
- margin: 5px 0 0 0;
-}
-#hideIgnoredTagsCb {
- margin: 0 2px 0 1px;
-}
-#recaptcha_widget_div {
- width:318px;
- float:left;
- clear:both;
-}
-p.signup_p {
- margin: 20px 0px 0px 0px;
-}
-.simple-subscribe-options ul {
- list-style:none;
- list-style-position:outside;
- margin:0;
-}
diff --git a/templates/fbconnect/xd_receiver.html b/templates/fbconnect/xd_receiver.html
deleted file mode 100755
index c67c57b7..00000000
--- a/templates/fbconnect/xd_receiver.html
+++ /dev/null
@@ -1 +0,0 @@
-<!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/templates/index.html b/templates/index.html
deleted file mode 100644
index 4041b863..00000000
--- a/templates/index.html
+++ /dev/null
@@ -1,164 +0,0 @@
-{% extends "base.html" %}
-<!-- index.html -->
-{% load i18n %}
-{% load extra_tags %}
-{% load humanize %}
-{% load extra_filters %}
-{% load smart_if %}
-{% block title %}{% spaceless %}{% trans "Home" %}{% endspaceless %}{% endblock %}
-{% block meta %}<meta name="keywords" content="{{ settings.APP_KEYWORDS }}" />
- <meta name="description" content="{{ settings.APP_DESCRIPTION }}" />{% endblock %}
-{% block forejs %}
- <script type="text/javascript">
- var tags = {{ tags_autocomplete|safe }};
- $().ready(function(){
- var tab_id = "{{ tab_id }}";
- $("#"+tab_id).attr('className',"on");
- $("#nav_questions").attr('className',"on");
- });
- </script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
- <script type='text/javascript' src='{% href "/content/js/com.cnprog.tag_selector.js" %}'></script>
-{% endblock %}
-{% block content %}
-<div class="tabBar">
- <div class="headQuestions">{% trans "Questions" %}</div>
- <div class="tabsA">
- <a id="latest" href="{% url questions %}?sort=latest" title="{% trans "last updated questions" %}" >{% trans "newest" %}</a>
- <a id="hottest" href="{% url questions %}?sort=hottest" title="{% trans "hottest questions" %}" >{% trans "hottest" %}</a>
- <a id="mostvoted" href="{% url questions %}?sort=mostvoted" title="{% trans "most voted questions" %}" >{% trans "most voted" %}</a>
- <a id="all" href="{% url questions %}" title="{% trans "all questions" %}" >{% trans "all questions" %}</a>
- </div>
-</div>
-<!-- 问题列表 -->
-<div id="listA">
- {% for question in questions %}
- <div class="qstA">
- <h2>
- <a href="{{ question.get_absolute_url }}">{{ question.get_question_title }}</a>
- </h2>
- <div class="stat">
- <table>
- <tr>
- <td><span class="num">{{ question.answer_count|intcomma }}</span> </td>
- <td><span class="num">{{ question.score|intcomma }}</span> </td>
- <td><span class="num">{{ question.view_count|cnprog_intword|safe }}</span> </td>
- </tr>
- <tr>
- <td><span class="unit">{% trans "answers" %}</span></td>
- <td><span class="unit">{% trans "votes" %}</span></td>
- <td><span class="unit">{% trans "views" %}</span></td>
- </tr>
- </table>
- </div>
-
- <div class="summary">
- {{ question.summary }}...
- </div>
-
- {% ifequal tab_id 'active'%}
- {% if question.wiki and settings.WIKI_ON %}
- <span class="from wiki">{% trans "community wiki" %}</span>
- <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span>
- {% else %}
- <div class="from">
- {% comment %}{% gravatar question.last_activity_by 24 %}{% endcomment %}
- <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span>
- <span class="score">{% get_score_badge question.last_activity_by %} </span>
- <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span>
- </div>
- {% endif %}
- {% else %}
- {% if question.wiki and settings.WIKI_ON %}
- <span class="from wiki">{% trans "community wiki" %}</span>
- <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span>
- {% else %}
- <div class="from">
- {% comment %}{% gravatar question.author 24 %}{% endcomment %}
- {% if question.last_activity_at != question.added_at %}
- {% if question.author.id != question.last_activity_by.id %}
- {% trans "Posted:" %}
- <span class="author"><a href="{{ question.author.get_profile_url }}">{{ question.author }}</a></span>
- <span class="score">{% get_score_badge question.author %} </span>
- / {% trans "Updated:" %}
- <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span>
- <span class="score">{% get_score_badge question.last_activity_by %} </span>
- <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span>
- {% else %}
- {% trans "Updated:" %}
- <span class="author"><a href="{{ question.last_activity_by.get_profile_url }}">{{ question.last_activity_by }}</a></span>
- <span class="score">{% get_score_badge question.last_activity_by %} </span>
- <span class="date" title="{{ question.last_activity_at }}">{% diff_date question.last_activity_at %}</span>
- {% endif %}
- {% else %}
- {% trans "Posted:" %}
- <span class="author"><a href="{{ question.author.get_profile_url }}">{{ question.author }}</a></span>
- <span class="score">{% get_score_badge question.author %} </span>
- <span class="date" title="{{ question.added_at }}">{% diff_date question.added_at %}</span>
- {% endif %}
- </div>
- {% endif %}
- {% endifequal %}
-
- <div class="tags">
- {% for tag in question.tagname_list %}
- <a href="{% url forum.views.tag tag|urlencode %}" title="{% trans "see questions tagged" %}'{{ tag }}'{% trans "using tags" %}" rel="tag">{{ tag }}</a>
- {% endfor %}
- </div>
- </div>
- {% endfor %}
-</div>
-{% endblock %}
-
-{% block sidebar %}
-{% if not request.user.is_authenticated %}
-<div class="boxA">
- <h3>{% trans "welcome to website" %}</h3>
- <div class="body">
- {{ settings.APP_INTRO|safe }}
- <div class="more"><a href="{% url about %}">{% trans "about" %} »</a></div>
- <div class="more"><a href="{% url faq %}">{% trans "faq" %} »</a></div>
- </div>
-</div>
-{% else %}
-{% include "tag_selector.html" %}
-{% endif %}
-<div class="boxC">
- <h3>{% trans "Recent tags" %}</h3>
- <div class="body">
- <div class="tags">
- {% for tag in tags %}
- <a rel="tag"
- title="{% blocktrans with tag.name as tagname %}see questions tagged '{{tagname}}'{% endblocktrans %}" href="{% url forum.views.tag tag.name|urlencode %}">{{ tag.name }}</a>
- {% endfor %}
- </div>
- <div class="more"><a href="{% url tags %}">{% trans "popular tags" %} »</a> </div>
- </div>
-</div>
-{% if awards %}
-<div class="boxC">
- <h3>{% trans "Recent awards" %}</h3>
- <div class="body">
- <ul class="badge-list">
- {% for award in awards %}
- <li>
- <a href="{% url badges %}{{award.badge_id}}/{{award.badge_name}}" title="{{ award.badge_description }}" class="medal">
- <span class="badge{{ award.badge_type }}">&#9679;</span>&nbsp;{{ award.badge_name }}</a> {% trans "given to" %}
- <a href="{% url users %}{{award.user_id}}/{{award.user_name}}">{{ award.user_name }}</a>
- </li>
- {% endfor %}
- </ul>
- <div class="more"><a href="{% url badges %}">{% trans "all awards" %} »</a> </div>
- </div>
-</div>
-{% endif %}
-<div id="feeds">
-<a href="{% href "/feeds/rss" %}" title="{% trans "subscribe to last 30 questions by RSS" %}">{% trans "subscribe to the questions feed" %}</a>
-</div>
-{% endblock %}
-{% block tail %}
-<div style="padding:5px 0 5px 5px;">
-<span class="evenMore">{% trans "Still looking for more? See" %} <a href="{% url questions %}">{% trans "complete list of questions" %}</a> {% trans "or" %} <a href="{% url tags %}">{% trans "popular tags" %}</a>{% trans "." %} {% trans "Please help us answer" %} <a href="{% url questions %}unanswered">{% trans "list of unanswered questions" %}</a>{% trans "." %}</span>
-</div>
-{% endblock %}
-<!-- index.html -->
diff --git a/user_messages/models.py b/user_messages/models.py
deleted file mode 100644
index b67ead6d..00000000
--- a/user_messages/models.py
+++ /dev/null
@@ -1,3 +0,0 @@
-"""
-blank models.py
-"""