diff options
-rw-r--r-- | .gitignore | 6 | ||||
-rwxr-xr-x | .idea/compiler.xml | 24 | ||||
-rwxr-xr-x | .idea/copyright/profiles_settings.xml | 5 | ||||
-rwxr-xr-x | .idea/encodings.xml | 5 | ||||
-rwxr-xr-x | .idea/misc.xml | 127 | ||||
-rwxr-xr-x | .idea/modules.xml | 9 | ||||
-rwxr-xr-x | .idea/uiDesigner.xml | 125 | ||||
-rwxr-xr-x | .idea/vcs.xml | 7 | ||||
-rwxr-xr-x | .idea/workspace.xml | 687 | ||||
-rw-r--r-- | INSTALL | 153 | ||||
-rw-r--r-- | PENDING | 31 | ||||
-rw-r--r-- | WISH_LIST (renamed from TODO) | 12 | ||||
-rw-r--r-- | context.py | 1 | ||||
-rw-r--r--[-rwxr-xr-x] | cron/send_email_alerts | 14 | ||||
-rw-r--r-- | cron/send_email_alerts_virtualenv | 10 | ||||
-rw-r--r-- | django_authopenid/forms.py | 4 | ||||
-rw-r--r-- | django_authopenid/util.py | 2 | ||||
-rwxr-xr-x | django_authopenid/views.py | 2 | ||||
-rwxr-xr-x | fbconnect/fb.py | 9 | ||||
-rwxr-xr-x | fbconnect/urls.py | 5 | ||||
-rwxr-xr-x | fbconnect/views.py | 33 | ||||
-rw-r--r-- | forum/__init__.py | 1 | ||||
-rw-r--r-- | forum/admin.py | 20 | ||||
-rw-r--r-- | forum/auth.py | 2 | ||||
-rw-r--r-- | forum/forms.py | 3 | ||||
-rw-r--r-- | forum/forms.py.orig | 352 | ||||
-rw-r--r-- | forum/management/__init__.py | 3 | ||||
-rw-r--r-- | forum/management/commands/send_email_alerts.py | 3 | ||||
-rw-r--r-- | forum/managers.py | 241 | ||||
-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.py | 964 | ||||
-rwxr-xr-x | forum/models/__init__.py | 341 | ||||
-rwxr-xr-x | forum/models/answer.py | 133 | ||||
-rwxr-xr-x | forum/models/base.py | 139 | ||||
-rwxr-xr-x | forum/models/meta.py | 89 | ||||
-rwxr-xr-x | forum/models/question.py | 335 | ||||
-rwxr-xr-x | forum/models/repute.py | 109 | ||||
-rwxr-xr-x | forum/models/tag.py | 85 | ||||
-rwxr-xr-x | forum/models/user.py | 67 | ||||
-rwxr-xr-x | forum/modules.py | 54 | ||||
-rw-r--r-- | forum/skins/README | 22 | ||||
-rw-r--r-- | forum/skins/__init__.py | 57 | ||||
-rw-r--r-- | forum/skins/common/media/README | 1 | ||||
-rw-r--r-- | forum/skins/default/media/images/blue-up-arrow-h18px.png (renamed from templates/content/images/blue-up-arrow-h18px.png) | bin | 593 -> 593 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/box-arrow.gif (renamed from templates/content/images/box-arrow.gif) | bin | 69 -> 69 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/bullet_green.gif (renamed from templates/content/images/bullet_green.gif) | bin | 64 -> 64 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/cc-88x31.png (renamed from templates/content/images/cc-88x31.png) | bin | 5460 -> 5460 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/cc-wiki.png (renamed from templates/content/images/cc-wiki.png) | bin | 2333 -> 2333 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/close-small-dark.png (renamed from templates/content/images/close-small-dark.png) | bin | 226 -> 226 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/close-small-hover.png (renamed from templates/content/images/close-small-hover.png) | bin | 337 -> 337 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/close-small.png (renamed from templates/content/images/close-small.png) | bin | 293 -> 293 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/dash.gif (renamed from templates/content/images/dash.gif) | bin | 44 -> 44 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/djangomade124x25_grey.gif (renamed from templates/content/images/djangomade124x25_grey.gif) | bin | 2035 -> 2035 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/dot-g.gif (renamed from templates/content/images/dot-g.gif) | bin | 61 -> 61 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/dot-list.gif (renamed from templates/content/images/dot-list.gif) | bin | 56 -> 56 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/edit.png (renamed from templates/content/images/edit.png) | bin | 758 -> 758 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/expander-arrow-hide.gif (renamed from templates/content/images/expander-arrow-hide.gif) | bin | 126 -> 126 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/expander-arrow-show.gif (renamed from templates/content/images/expander-arrow-show.gif) | bin | 135 -> 135 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/favicon.gif (renamed from templates/content/images/favicon.gif) | bin | 3918 -> 3918 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/feed-icon-small.png (renamed from templates/content/images/feed-icon-small.png) | bin | 689 -> 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) | bin | 383 -> 383 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/grippie.png (renamed from templates/content/images/grippie.png) | bin | 162 -> 162 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/indicator.gif (renamed from templates/content/images/indicator.gif) | bin | 2545 -> 2545 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/logo.gif (renamed from templates/content/images/logo.gif) | bin | 2114 -> 2114 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/logo.png (renamed from templates/content/images/logo.png) | bin | 2081 -> 2081 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/logo1.png (renamed from templates/content/images/logo1.png) | bin | 2752 -> 2752 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/logo2.png (renamed from templates/content/images/logo2.png) | bin | 2124 -> 2124 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/medala.gif (renamed from templates/content/images/medala.gif) | bin | 801 -> 801 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/medala_on.gif (renamed from templates/content/images/medala_on.gif) | bin | 957 -> 957 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/new.gif (renamed from templates/content/images/new.gif) | bin | 635 -> 635 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/nophoto.png (renamed from templates/content/images/nophoto.png) | bin | 696 -> 696 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid.gif (renamed from templates/content/images/openid.gif) | bin | 910 -> 910 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/aol.gif (renamed from templates/content/images/openid/aol.gif) | bin | 2205 -> 2205 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/blogger.ico (renamed from templates/content/images/openid/blogger.ico) | bin | 3638 -> 3638 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/claimid.ico (renamed from templates/content/images/openid/claimid.ico) | bin | 3638 -> 3638 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/facebook.gif (renamed from templates/content/images/openid/facebook.gif) | bin | 2075 -> 2075 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/flickr.ico (renamed from templates/content/images/openid/flickr.ico) | bin | 1150 -> 1150 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/google.gif (renamed from templates/content/images/openid/google.gif) | bin | 1596 -> 1596 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/livejournal.ico (renamed from templates/content/images/openid/livejournal.ico) | bin | 5222 -> 5222 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/myopenid.ico (renamed from templates/content/images/openid/myopenid.ico) | bin | 2862 -> 2862 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/openid-inputicon.gif (renamed from templates/content/images/openid/openid-inputicon.gif) | bin | 237 -> 237 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/openid.gif (renamed from templates/content/images/openid/openid.gif) | bin | 740 -> 740 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/technorati.ico (renamed from templates/content/images/openid/technorati.ico) | bin | 2294 -> 2294 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/verisign.ico (renamed from templates/content/images/openid/verisign.ico) | bin | 4710 -> 4710 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/vidoop.ico (renamed from templates/content/images/openid/vidoop.ico) | bin | 1406 -> 1406 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/wordpress.ico (renamed from templates/content/images/openid/wordpress.ico) | bin | 1150 -> 1150 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/openid/yahoo.gif (renamed from templates/content/images/openid/yahoo.gif) | bin | 1682 -> 1682 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/quest-bg.gif (renamed from templates/content/images/quest-bg.gif) | bin | 294 -> 294 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/vote-accepted-on.png (renamed from templates/content/images/vote-accepted-on.png) | bin | 1124 -> 1124 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/vote-accepted.png (renamed from templates/content/images/vote-accepted.png) | bin | 1058 -> 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) | bin | 905 -> 905 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/vote-arrow-down.png (renamed from templates/content/images/vote-arrow-down.png) | bin | 876 -> 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) | bin | 906 -> 906 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/vote-arrow-up.png (renamed from templates/content/images/vote-arrow-up.png) | bin | 843 -> 843 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/vote-favorite-off.png (renamed from templates/content/images/vote-favorite-off.png) | bin | 930 -> 930 bytes | |||
-rw-r--r-- | forum/skins/default/media/images/vote-favorite-on.png (renamed from templates/content/images/vote-favorite-on.png) | bin | 1023 -> 1023 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/aol.gif (renamed from templates/content/jquery-openid/images/aol.gif) | bin | 2205 -> 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) | bin | 432 -> 432 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/blogger.ico (renamed from templates/content/jquery-openid/images/blogger.ico) | bin | 3638 -> 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) | bin | 629 -> 629 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/claimid.ico (renamed from templates/content/jquery-openid/images/claimid.ico) | bin | 3638 -> 3638 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/facebook.gif (renamed from templates/content/jquery-openid/images/facebook.gif) | bin | 2075 -> 2075 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/flickr.ico (renamed from templates/content/jquery-openid/images/flickr.ico) | bin | 1150 -> 1150 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/flickr.png (renamed from templates/content/jquery-openid/images/flickr.png) | bin | 426 -> 426 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/google.gif (renamed from templates/content/jquery-openid/images/google.gif) | bin | 1596 -> 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) | bin | 713 -> 713 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/livejournal.ico (renamed from templates/content/jquery-openid/images/livejournal.ico) | bin | 5222 -> 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) | bin | 511 -> 511 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/myopenid.ico (renamed from templates/content/jquery-openid/images/myopenid.ico) | bin | 2862 -> 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) | bin | 237 -> 237 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/openid.gif (renamed from templates/content/jquery-openid/images/openid.gif) | bin | 740 -> 740 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/openidico.png (renamed from templates/content/jquery-openid/images/openidico.png) | bin | 654 -> 654 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/openidico16.png (renamed from templates/content/jquery-openid/images/openidico16.png) | bin | 554 -> 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) | bin | 606 -> 606 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/technorati.ico (renamed from templates/content/jquery-openid/images/technorati.ico) | bin | 2294 -> 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) | bin | 859 -> 859 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/verisign.ico (renamed from templates/content/jquery-openid/images/verisign.ico) | bin | 4710 -> 4710 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/vidoop.ico (renamed from templates/content/jquery-openid/images/vidoop.ico) | bin | 1406 -> 1406 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/vidoop.png (renamed from templates/content/jquery-openid/images/vidoop.png) | bin | 499 -> 499 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/wordpress.ico (renamed from templates/content/jquery-openid/images/wordpress.ico) | bin | 1150 -> 1150 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/wordpress.png (renamed from templates/content/jquery-openid/images/wordpress.png) | bin | 566 -> 566 bytes | |||
-rw-r--r-- | forum/skins/default/media/jquery-openid/images/yahoo.gif (renamed from templates/content/jquery-openid/images/yahoo.gif) | bin | 1682 -> 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) | bin | 7465 -> 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.js | 1 | ||||
-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) | bin | 851219 -> 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.css | 2459 | ||||
-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-x | forum/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-x | forum/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-x | forum/skins/default/templates/fbconnect/xd_receiver.html | 10 | ||||
-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-x | forum/skins/default/templates/index.html | 124 | ||||
-rwxr-xr-x | forum/skins/default/templates/index_.html | 124 | ||||
-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.py | 5 | ||||
-rw-r--r-- | forum/templatetags/extra_tags.py | 36 | ||||
-rw-r--r-- | forum/upfiles/README | 2 | ||||
-rw-r--r-- | forum/urls.py | 112 | ||||
-rw-r--r-- | forum/user.py | 74 | ||||
-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.py | 2412 | ||||
-rw-r--r-- | forum/views/README | 12 | ||||
-rw-r--r-- | forum/views/__init__.py | 5 | ||||
-rw-r--r-- | forum/views/commands.py | 335 | ||||
-rw-r--r-- | forum/views/meta.py | 91 | ||||
-rw-r--r-- | forum/views/readers.py | 588 | ||||
-rw-r--r-- | forum/views/users.py | 947 | ||||
-rw-r--r-- | forum/views/writers.py | 442 | ||||
-rwxr-xr-x[-rw-r--r--] | forum_modules/__init__.py (renamed from utils/__init__.py) | 0 | ||||
-rwxr-xr-x | forum_modules/books/__init__.py | 3 | ||||
-rwxr-xr-x | forum_modules/books/models.py | 63 | ||||
-rwxr-xr-x | forum_modules/books/urls.py | 10 | ||||
-rwxr-xr-x | forum_modules/books/views.py | 142 | ||||
-rwxr-xr-x | forum_modules/pgfulltext/__init__.py | 9 | ||||
-rwxr-xr-x | forum_modules/pgfulltext/handlers.py | 11 | ||||
-rwxr-xr-x[-rw-r--r--] | forum_modules/pgfulltext/management.py (renamed from pgfulltext/management.py) | 18 | ||||
-rwxr-xr-x | forum_modules/pgfulltext/pg_fts_install.sql | 38 | ||||
-rwxr-xr-x | forum_modules/sphinxfulltext/DISABLED | 0 | ||||
-rwxr-xr-x | forum_modules/sphinxfulltext/__init__.py | 0 | ||||
-rwxr-xr-x | forum_modules/sphinxfulltext/dependencies.py | 2 | ||||
-rwxr-xr-x | forum_modules/sphinxfulltext/handlers.py | 4 | ||||
-rwxr-xr-x | forum_modules/sphinxfulltext/models.py | 10 | ||||
-rwxr-xr-x | forum_modules/sphinxfulltext/settings.py | 5 | ||||
-rw-r--r-- | locale/en/LC_MESSAGES/django.mo | bin | 26619 -> 26986 bytes | |||
-rw-r--r-- | locale/en/LC_MESSAGES/django.po | 2200 | ||||
-rw-r--r-- | locale/es/LC_MESSAGES/django.mo | bin | 49713 -> 367 bytes | |||
-rw-r--r-- | locale/es/LC_MESSAGES/django.po | 3213 | ||||
-rwxr-xr-x | osqa.iml | 19 | ||||
-rw-r--r-- | session_messages/__init__.py | 37 | ||||
-rw-r--r-- | session_messages/context_processors.py | 48 | ||||
-rw-r--r-- | session_messages/models.py | 3 | ||||
-rwxr-xr-x | settings.py | 14 | ||||
-rwxr-xr-x | settings_local.py.dist | 21 | ||||
-rw-r--r-- | sql_scripts/update_2010_02_22.sql | 1 | ||||
-rw-r--r-- | templates/content/js/wmd/wmd-min.js | 1 | ||||
-rw-r--r-- | templates/content/style/style.css | 1466 | ||||
-rwxr-xr-x | templates/fbconnect/xd_receiver.html | 1 | ||||
-rw-r--r-- | templates/index.html | 164 | ||||
-rw-r--r-- | user_messages/models.py | 3 |
283 files changed, 11385 insertions, 8622 deletions
@@ -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="<template>" 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>
+
@@ -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. @@ -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: @@ -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 Binary files differindex e1f29e86..e1f29e86 100644 --- a/templates/content/images/blue-up-arrow-h18px.png +++ b/forum/skins/default/media/images/blue-up-arrow-h18px.png diff --git a/templates/content/images/box-arrow.gif b/forum/skins/default/media/images/box-arrow.gif Binary files differindex 89dcf5b3..89dcf5b3 100644 --- a/templates/content/images/box-arrow.gif +++ b/forum/skins/default/media/images/box-arrow.gif diff --git a/templates/content/images/bullet_green.gif b/forum/skins/default/media/images/bullet_green.gif Binary files differindex fa530910..fa530910 100644 --- a/templates/content/images/bullet_green.gif +++ b/forum/skins/default/media/images/bullet_green.gif diff --git a/templates/content/images/cc-88x31.png b/forum/skins/default/media/images/cc-88x31.png Binary files differindex 0f2a0f10..0f2a0f10 100644 --- a/templates/content/images/cc-88x31.png +++ b/forum/skins/default/media/images/cc-88x31.png diff --git a/templates/content/images/cc-wiki.png b/forum/skins/default/media/images/cc-wiki.png Binary files differindex 3e680538..3e680538 100644 --- a/templates/content/images/cc-wiki.png +++ b/forum/skins/default/media/images/cc-wiki.png diff --git a/templates/content/images/close-small-dark.png b/forum/skins/default/media/images/close-small-dark.png Binary files differindex 280c1fc7..280c1fc7 100644 --- a/templates/content/images/close-small-dark.png +++ b/forum/skins/default/media/images/close-small-dark.png diff --git a/templates/content/images/close-small-hover.png b/forum/skins/default/media/images/close-small-hover.png Binary files differindex 7899aec7..7899aec7 100644 --- a/templates/content/images/close-small-hover.png +++ b/forum/skins/default/media/images/close-small-hover.png diff --git a/templates/content/images/close-small.png b/forum/skins/default/media/images/close-small.png Binary files differindex 5a99d31f..5a99d31f 100644 --- a/templates/content/images/close-small.png +++ b/forum/skins/default/media/images/close-small.png diff --git a/templates/content/images/dash.gif b/forum/skins/default/media/images/dash.gif Binary files differindex d1ddc507..d1ddc507 100644 --- a/templates/content/images/dash.gif +++ b/forum/skins/default/media/images/dash.gif diff --git a/templates/content/images/djangomade124x25_grey.gif b/forum/skins/default/media/images/djangomade124x25_grey.gif Binary files differindex d34bb311..d34bb311 100644 --- a/templates/content/images/djangomade124x25_grey.gif +++ b/forum/skins/default/media/images/djangomade124x25_grey.gif diff --git a/templates/content/images/dot-g.gif b/forum/skins/default/media/images/dot-g.gif Binary files differindex 5d6bb28e..5d6bb28e 100644 --- a/templates/content/images/dot-g.gif +++ b/forum/skins/default/media/images/dot-g.gif diff --git a/templates/content/images/dot-list.gif b/forum/skins/default/media/images/dot-list.gif Binary files differindex f6a6b865..f6a6b865 100644 --- a/templates/content/images/dot-list.gif +++ b/forum/skins/default/media/images/dot-list.gif diff --git a/templates/content/images/edit.png b/forum/skins/default/media/images/edit.png Binary files differindex dcb09be0..dcb09be0 100644 --- a/templates/content/images/edit.png +++ b/forum/skins/default/media/images/edit.png diff --git a/templates/content/images/expander-arrow-hide.gif b/forum/skins/default/media/images/expander-arrow-hide.gif Binary files differindex feb6a618..feb6a618 100644 --- a/templates/content/images/expander-arrow-hide.gif +++ b/forum/skins/default/media/images/expander-arrow-hide.gif diff --git a/templates/content/images/expander-arrow-show.gif b/forum/skins/default/media/images/expander-arrow-show.gif Binary files differindex 6825c56e..6825c56e 100644 --- a/templates/content/images/expander-arrow-show.gif +++ b/forum/skins/default/media/images/expander-arrow-show.gif diff --git a/templates/content/images/favicon.gif b/forum/skins/default/media/images/favicon.gif Binary files differindex 910c2666..910c2666 100644 --- a/templates/content/images/favicon.gif +++ b/forum/skins/default/media/images/favicon.gif diff --git a/templates/content/images/feed-icon-small.png b/forum/skins/default/media/images/feed-icon-small.png Binary files differindex b3c949d2..b3c949d2 100644 --- a/templates/content/images/feed-icon-small.png +++ b/forum/skins/default/media/images/feed-icon-small.png diff --git a/templates/content/images/gray-up-arrow-h18px.png b/forum/skins/default/media/images/gray-up-arrow-h18px.png Binary files differindex 78767445..78767445 100644 --- a/templates/content/images/gray-up-arrow-h18px.png +++ b/forum/skins/default/media/images/gray-up-arrow-h18px.png diff --git a/templates/content/images/grippie.png b/forum/skins/default/media/images/grippie.png Binary files differindex 6524d416..6524d416 100644 --- a/templates/content/images/grippie.png +++ b/forum/skins/default/media/images/grippie.png diff --git a/templates/content/images/indicator.gif b/forum/skins/default/media/images/indicator.gif Binary files differindex 1c72ebb5..1c72ebb5 100644 --- a/templates/content/images/indicator.gif +++ b/forum/skins/default/media/images/indicator.gif diff --git a/templates/content/images/logo.gif b/forum/skins/default/media/images/logo.gif Binary files differindex ab690de2..ab690de2 100644 --- a/templates/content/images/logo.gif +++ b/forum/skins/default/media/images/logo.gif diff --git a/templates/content/images/logo.png b/forum/skins/default/media/images/logo.png Binary files differindex 6a250e35..6a250e35 100644 --- a/templates/content/images/logo.png +++ b/forum/skins/default/media/images/logo.png diff --git a/templates/content/images/logo1.png b/forum/skins/default/media/images/logo1.png Binary files differindex d79a6271..d79a6271 100644 --- a/templates/content/images/logo1.png +++ b/forum/skins/default/media/images/logo1.png diff --git a/templates/content/images/logo2.png b/forum/skins/default/media/images/logo2.png Binary files differindex bd3cccd9..bd3cccd9 100644 --- a/templates/content/images/logo2.png +++ b/forum/skins/default/media/images/logo2.png diff --git a/templates/content/images/medala.gif b/forum/skins/default/media/images/medala.gif Binary files differindex 93dd1a39..93dd1a39 100644 --- a/templates/content/images/medala.gif +++ b/forum/skins/default/media/images/medala.gif diff --git a/templates/content/images/medala_on.gif b/forum/skins/default/media/images/medala_on.gif Binary files differindex a18f9e85..a18f9e85 100644 --- a/templates/content/images/medala_on.gif +++ b/forum/skins/default/media/images/medala_on.gif diff --git a/templates/content/images/new.gif b/forum/skins/default/media/images/new.gif Binary files differindex 8a220b53..8a220b53 100644 --- a/templates/content/images/new.gif +++ b/forum/skins/default/media/images/new.gif diff --git a/templates/content/images/nophoto.png b/forum/skins/default/media/images/nophoto.png Binary files differindex 2daf0ffd..2daf0ffd 100644 --- a/templates/content/images/nophoto.png +++ b/forum/skins/default/media/images/nophoto.png diff --git a/templates/content/images/openid.gif b/forum/skins/default/media/images/openid.gif Binary files differindex 8540e12b..8540e12b 100644 --- a/templates/content/images/openid.gif +++ b/forum/skins/default/media/images/openid.gif diff --git a/templates/content/images/openid/aol.gif b/forum/skins/default/media/images/openid/aol.gif Binary files differindex decc4f12..decc4f12 100644 --- a/templates/content/images/openid/aol.gif +++ b/forum/skins/default/media/images/openid/aol.gif diff --git a/templates/content/images/openid/blogger.ico b/forum/skins/default/media/images/openid/blogger.ico Binary files differindex 1b9730b0..1b9730b0 100644 --- a/templates/content/images/openid/blogger.ico +++ b/forum/skins/default/media/images/openid/blogger.ico diff --git a/templates/content/images/openid/claimid.ico b/forum/skins/default/media/images/openid/claimid.ico Binary files differindex 2b80f491..2b80f491 100644 --- a/templates/content/images/openid/claimid.ico +++ b/forum/skins/default/media/images/openid/claimid.ico diff --git a/templates/content/images/openid/facebook.gif b/forum/skins/default/media/images/openid/facebook.gif Binary files differindex b997b358..b997b358 100644 --- a/templates/content/images/openid/facebook.gif +++ b/forum/skins/default/media/images/openid/facebook.gif diff --git a/templates/content/images/openid/flickr.ico b/forum/skins/default/media/images/openid/flickr.ico Binary files differindex 11f6e07f..11f6e07f 100644 --- a/templates/content/images/openid/flickr.ico +++ b/forum/skins/default/media/images/openid/flickr.ico diff --git a/templates/content/images/openid/google.gif b/forum/skins/default/media/images/openid/google.gif Binary files differindex 1b6cd07b..1b6cd07b 100644 --- a/templates/content/images/openid/google.gif +++ b/forum/skins/default/media/images/openid/google.gif diff --git a/templates/content/images/openid/livejournal.ico b/forum/skins/default/media/images/openid/livejournal.ico Binary files differindex f3d21ec5..f3d21ec5 100644 --- a/templates/content/images/openid/livejournal.ico +++ b/forum/skins/default/media/images/openid/livejournal.ico diff --git a/templates/content/images/openid/myopenid.ico b/forum/skins/default/media/images/openid/myopenid.ico Binary files differindex ceb06e6a..ceb06e6a 100644 --- a/templates/content/images/openid/myopenid.ico +++ b/forum/skins/default/media/images/openid/myopenid.ico diff --git a/templates/content/images/openid/openid-inputicon.gif b/forum/skins/default/media/images/openid/openid-inputicon.gif Binary files differindex cde836c8..cde836c8 100644 --- a/templates/content/images/openid/openid-inputicon.gif +++ b/forum/skins/default/media/images/openid/openid-inputicon.gif diff --git a/templates/content/images/openid/openid.gif b/forum/skins/default/media/images/openid/openid.gif Binary files differindex c718b0e6..c718b0e6 100644 --- a/templates/content/images/openid/openid.gif +++ b/forum/skins/default/media/images/openid/openid.gif diff --git a/templates/content/images/openid/technorati.ico b/forum/skins/default/media/images/openid/technorati.ico Binary files differindex fa1083c1..fa1083c1 100644 --- a/templates/content/images/openid/technorati.ico +++ b/forum/skins/default/media/images/openid/technorati.ico diff --git a/templates/content/images/openid/verisign.ico b/forum/skins/default/media/images/openid/verisign.ico Binary files differindex 3953af93..3953af93 100644 --- a/templates/content/images/openid/verisign.ico +++ b/forum/skins/default/media/images/openid/verisign.ico diff --git a/templates/content/images/openid/vidoop.ico b/forum/skins/default/media/images/openid/vidoop.ico Binary files differindex bbd9a0d5..bbd9a0d5 100644 --- a/templates/content/images/openid/vidoop.ico +++ b/forum/skins/default/media/images/openid/vidoop.ico diff --git a/templates/content/images/openid/wordpress.ico b/forum/skins/default/media/images/openid/wordpress.ico Binary files differindex 31b7d2c2..31b7d2c2 100644 --- a/templates/content/images/openid/wordpress.ico +++ b/forum/skins/default/media/images/openid/wordpress.ico diff --git a/templates/content/images/openid/yahoo.gif b/forum/skins/default/media/images/openid/yahoo.gif Binary files differindex 42adbfa5..42adbfa5 100644 --- a/templates/content/images/openid/yahoo.gif +++ b/forum/skins/default/media/images/openid/yahoo.gif diff --git a/templates/content/images/quest-bg.gif b/forum/skins/default/media/images/quest-bg.gif Binary files differindex b7540238..b7540238 100644 --- a/templates/content/images/quest-bg.gif +++ b/forum/skins/default/media/images/quest-bg.gif diff --git a/templates/content/images/vote-accepted-on.png b/forum/skins/default/media/images/vote-accepted-on.png Binary files differindex 2026f3bc..2026f3bc 100644 --- a/templates/content/images/vote-accepted-on.png +++ b/forum/skins/default/media/images/vote-accepted-on.png diff --git a/templates/content/images/vote-accepted.png b/forum/skins/default/media/images/vote-accepted.png Binary files differindex ecd18551..ecd18551 100644 --- a/templates/content/images/vote-accepted.png +++ b/forum/skins/default/media/images/vote-accepted.png diff --git a/templates/content/images/vote-arrow-down-on.png b/forum/skins/default/media/images/vote-arrow-down-on.png Binary files differindex 048dbb44..048dbb44 100644 --- a/templates/content/images/vote-arrow-down-on.png +++ b/forum/skins/default/media/images/vote-arrow-down-on.png diff --git a/templates/content/images/vote-arrow-down.png b/forum/skins/default/media/images/vote-arrow-down.png Binary files differindex e4fdec0a..e4fdec0a 100644 --- a/templates/content/images/vote-arrow-down.png +++ b/forum/skins/default/media/images/vote-arrow-down.png diff --git a/templates/content/images/vote-arrow-up-on.png b/forum/skins/default/media/images/vote-arrow-up-on.png Binary files differindex 56ad0c25..56ad0c25 100644 --- a/templates/content/images/vote-arrow-up-on.png +++ b/forum/skins/default/media/images/vote-arrow-up-on.png diff --git a/templates/content/images/vote-arrow-up.png b/forum/skins/default/media/images/vote-arrow-up.png Binary files differindex 6e9a51c7..6e9a51c7 100644 --- a/templates/content/images/vote-arrow-up.png +++ b/forum/skins/default/media/images/vote-arrow-up.png diff --git a/templates/content/images/vote-favorite-off.png b/forum/skins/default/media/images/vote-favorite-off.png Binary files differindex c1bef074..c1bef074 100644 --- a/templates/content/images/vote-favorite-off.png +++ b/forum/skins/default/media/images/vote-favorite-off.png diff --git a/templates/content/images/vote-favorite-on.png b/forum/skins/default/media/images/vote-favorite-on.png Binary files differindex 1f9c14ab..1f9c14ab 100644 --- a/templates/content/images/vote-favorite-on.png +++ b/forum/skins/default/media/images/vote-favorite-on.png diff --git a/templates/content/jquery-openid/images/aol.gif b/forum/skins/default/media/jquery-openid/images/aol.gif Binary files differindex decc4f12..decc4f12 100644 --- a/templates/content/jquery-openid/images/aol.gif +++ b/forum/skins/default/media/jquery-openid/images/aol.gif diff --git a/templates/content/jquery-openid/images/blogger-1.png b/forum/skins/default/media/jquery-openid/images/blogger-1.png Binary files differindex 8b360ea5..8b360ea5 100644 --- a/templates/content/jquery-openid/images/blogger-1.png +++ b/forum/skins/default/media/jquery-openid/images/blogger-1.png diff --git a/templates/content/jquery-openid/images/blogger.ico b/forum/skins/default/media/jquery-openid/images/blogger.ico Binary files differindex 1b9730b0..1b9730b0 100644 --- a/templates/content/jquery-openid/images/blogger.ico +++ b/forum/skins/default/media/jquery-openid/images/blogger.ico diff --git a/templates/content/jquery-openid/images/claimid-0.png b/forum/skins/default/media/jquery-openid/images/claimid-0.png Binary files differindex 4a0ea1b3..4a0ea1b3 100644 --- a/templates/content/jquery-openid/images/claimid-0.png +++ b/forum/skins/default/media/jquery-openid/images/claimid-0.png diff --git a/templates/content/jquery-openid/images/claimid.ico b/forum/skins/default/media/jquery-openid/images/claimid.ico Binary files differindex 2b80f491..2b80f491 100644 --- a/templates/content/jquery-openid/images/claimid.ico +++ b/forum/skins/default/media/jquery-openid/images/claimid.ico diff --git a/templates/content/jquery-openid/images/facebook.gif b/forum/skins/default/media/jquery-openid/images/facebook.gif Binary files differindex b997b358..b997b358 100644 --- a/templates/content/jquery-openid/images/facebook.gif +++ b/forum/skins/default/media/jquery-openid/images/facebook.gif diff --git a/templates/content/jquery-openid/images/flickr.ico b/forum/skins/default/media/jquery-openid/images/flickr.ico Binary files differindex 11f6e07f..11f6e07f 100644 --- a/templates/content/jquery-openid/images/flickr.ico +++ b/forum/skins/default/media/jquery-openid/images/flickr.ico diff --git a/templates/content/jquery-openid/images/flickr.png b/forum/skins/default/media/jquery-openid/images/flickr.png Binary files differindex 142405a6..142405a6 100644 --- a/templates/content/jquery-openid/images/flickr.png +++ b/forum/skins/default/media/jquery-openid/images/flickr.png diff --git a/templates/content/jquery-openid/images/google.gif b/forum/skins/default/media/jquery-openid/images/google.gif Binary files differindex 1b6cd07b..1b6cd07b 100644 --- a/templates/content/jquery-openid/images/google.gif +++ b/forum/skins/default/media/jquery-openid/images/google.gif diff --git a/templates/content/jquery-openid/images/livejournal-1.png b/forum/skins/default/media/jquery-openid/images/livejournal-1.png Binary files differindex e6436081..e6436081 100644 --- a/templates/content/jquery-openid/images/livejournal-1.png +++ b/forum/skins/default/media/jquery-openid/images/livejournal-1.png diff --git a/templates/content/jquery-openid/images/livejournal.ico b/forum/skins/default/media/jquery-openid/images/livejournal.ico Binary files differindex f3d21ec5..f3d21ec5 100644 --- a/templates/content/jquery-openid/images/livejournal.ico +++ b/forum/skins/default/media/jquery-openid/images/livejournal.ico diff --git a/templates/content/jquery-openid/images/myopenid-2.png b/forum/skins/default/media/jquery-openid/images/myopenid-2.png Binary files differindex f64fb8e8..f64fb8e8 100644 --- a/templates/content/jquery-openid/images/myopenid-2.png +++ b/forum/skins/default/media/jquery-openid/images/myopenid-2.png diff --git a/templates/content/jquery-openid/images/myopenid.ico b/forum/skins/default/media/jquery-openid/images/myopenid.ico Binary files differindex ceb06e6a..ceb06e6a 100644 --- a/templates/content/jquery-openid/images/myopenid.ico +++ b/forum/skins/default/media/jquery-openid/images/myopenid.ico diff --git a/templates/content/jquery-openid/images/openid-inputicon.gif b/forum/skins/default/media/jquery-openid/images/openid-inputicon.gif Binary files differindex cde836c8..cde836c8 100644 --- a/templates/content/jquery-openid/images/openid-inputicon.gif +++ b/forum/skins/default/media/jquery-openid/images/openid-inputicon.gif diff --git a/templates/content/jquery-openid/images/openid.gif b/forum/skins/default/media/jquery-openid/images/openid.gif Binary files differindex c718b0e6..c718b0e6 100644 --- a/templates/content/jquery-openid/images/openid.gif +++ b/forum/skins/default/media/jquery-openid/images/openid.gif diff --git a/templates/content/jquery-openid/images/openidico.png b/forum/skins/default/media/jquery-openid/images/openidico.png Binary files differindex ab622669..ab622669 100644 --- a/templates/content/jquery-openid/images/openidico.png +++ b/forum/skins/default/media/jquery-openid/images/openidico.png diff --git a/templates/content/jquery-openid/images/openidico16.png b/forum/skins/default/media/jquery-openid/images/openidico16.png Binary files differindex ad718ac5..ad718ac5 100644 --- a/templates/content/jquery-openid/images/openidico16.png +++ b/forum/skins/default/media/jquery-openid/images/openidico16.png diff --git a/templates/content/jquery-openid/images/technorati-1.png b/forum/skins/default/media/jquery-openid/images/technorati-1.png Binary files differindex f7195240..f7195240 100644 --- a/templates/content/jquery-openid/images/technorati-1.png +++ b/forum/skins/default/media/jquery-openid/images/technorati-1.png diff --git a/templates/content/jquery-openid/images/technorati.ico b/forum/skins/default/media/jquery-openid/images/technorati.ico Binary files differindex fa1083c1..fa1083c1 100644 --- a/templates/content/jquery-openid/images/technorati.ico +++ b/forum/skins/default/media/jquery-openid/images/technorati.ico diff --git a/templates/content/jquery-openid/images/verisign-2.png b/forum/skins/default/media/jquery-openid/images/verisign-2.png Binary files differindex c1467008..c1467008 100644 --- a/templates/content/jquery-openid/images/verisign-2.png +++ b/forum/skins/default/media/jquery-openid/images/verisign-2.png diff --git a/templates/content/jquery-openid/images/verisign.ico b/forum/skins/default/media/jquery-openid/images/verisign.ico Binary files differindex 3953af93..3953af93 100644 --- a/templates/content/jquery-openid/images/verisign.ico +++ b/forum/skins/default/media/jquery-openid/images/verisign.ico diff --git a/templates/content/jquery-openid/images/vidoop.ico b/forum/skins/default/media/jquery-openid/images/vidoop.ico Binary files differindex bbd9a0d5..bbd9a0d5 100644 --- a/templates/content/jquery-openid/images/vidoop.ico +++ b/forum/skins/default/media/jquery-openid/images/vidoop.ico diff --git a/templates/content/jquery-openid/images/vidoop.png b/forum/skins/default/media/jquery-openid/images/vidoop.png Binary files differindex 032c9e98..032c9e98 100644 --- a/templates/content/jquery-openid/images/vidoop.png +++ b/forum/skins/default/media/jquery-openid/images/vidoop.png diff --git a/templates/content/jquery-openid/images/wordpress.ico b/forum/skins/default/media/jquery-openid/images/wordpress.ico Binary files differindex 31b7d2c2..31b7d2c2 100644 --- a/templates/content/jquery-openid/images/wordpress.ico +++ b/forum/skins/default/media/jquery-openid/images/wordpress.ico diff --git a/templates/content/jquery-openid/images/wordpress.png b/forum/skins/default/media/jquery-openid/images/wordpress.png Binary files differindex ee29f0cf..ee29f0cf 100644 --- a/templates/content/jquery-openid/images/wordpress.png +++ b/forum/skins/default/media/jquery-openid/images/wordpress.png diff --git a/templates/content/jquery-openid/images/yahoo.gif b/forum/skins/default/media/jquery-openid/images/yahoo.gif Binary files differindex 42adbfa5..42adbfa5 100644 --- a/templates/content/jquery-openid/images/yahoo.gif +++ b/forum/skins/default/media/jquery-openid/images/yahoo.gif 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 Binary files differindex 50b37090..50b37090 100644 --- a/templates/content/js/wmd/images/wmd-buttons.png +++ b/forum/skins/default/media/js/wmd/images/wmd-buttons.png 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,"&");Z=Z.replace(/</g,"<");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 Binary files differindex c29470bd..c29470bd 100644 --- a/templates/content/js/yuicompressor-2.4.2.jar +++ b/forum/skins/default/media/js/yuicompressor-2.4.2.jar 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 }}">●</span> {{ 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 }}">●</span> {{ 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">×{{ 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">× {{ 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> <span class="tag-number">× {{ 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">× {{ 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 Binary files differindex ef3007a0..20813cb3 100644 --- a/locale/en/LC_MESSAGES/django.mo +++ b/locale/en/LC_MESSAGES/django.mo 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&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&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&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&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&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/>• 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 Binary files differindex fc7ebe14..2b514069 100644 --- a/locale/es/LC_MESSAGES/django.mo +++ b/locale/es/LC_MESSAGES/django.mo 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,"&");Z=Z.replace(/</g,"<");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 }}">●</span> {{ 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 -""" |