From 8d6f7bc7837a4d0c1a912cdc9cf66177b04b1740 Mon Sep 17 00:00:00 2001
From: hernani
Date: Thu, 8 Apr 2010 15:20:37 +0000
Subject: [PATCH] cleaning the repo
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@11 0cfe37f9-358a-4d5e-be75-b63607b5c754
---
.project | 23 -
.pydevproject | 10 -
HOW_TO_DEBUG | 39 -
INSTALL | 314 --
PENDING | 28 -
README | 6 -
ROADMAP.rst | 32 -
WISH_LIST | 10 -
__init__.py | 0
context.py | 47 -
cron/send_email_alerts | 4 -
dos2unix.sh | 12 -
forum/__init__.py | 1 -
forum/admin.py | 74 -
forum/auth.py | 498 ---
forum/authentication/__init__.py | 27 -
forum/authentication/base.py | 40 -
forum/authentication/forms.py | 31 -
forum/const.py | 92 -
forum/feed.py | 43 -
forum/forms.py | 359 --
forum/management/__init__.py | 3 -
forum/management/commands/__init__.py | 0
forum/management/commands/base_command.py | 35 -
.../management/commands/clean_award_badges.py | 59 -
.../commands/message_to_everyone.py | 12 -
.../management/commands/multi_award_badges.py | 348 --
.../management/commands/once_award_badges.py | 350 --
forum/management/commands/sample_command.py | 7 -
.../management/commands/send_email_alerts.py | 192 -
.../management/commands/subscribe_everyone.py | 32 -
forum/middleware/__init__.py | 0
forum/middleware/anon_user.py | 35 -
forum/middleware/cancel.py | 15 -
forum/middleware/pagesize.py | 33 -
forum/models/__init__.py | 343 --
forum/models/answer.py | 134 -
forum/models/base.py | 139 -
forum/models/meta.py | 89 -
forum/models/question.py | 336 --
forum/models/repute.py | 109 -
forum/models/tag.py | 85 -
forum/models/user.py | 77 -
forum/modules.py | 79 -
forum/sitemap.py | 14 -
forum/skins/README | 22 -
forum/skins/__init__.py | 57 -
forum/skins/common/media/README | 1 -
.../media/images/blue-up-arrow-h18px.png | Bin 593 -> 0 bytes
.../skins/default/media/images/box-arrow.gif | Bin 69 -> 0 bytes
.../default/media/images/bullet_green.gif | Bin 64 -> 0 bytes
forum/skins/default/media/images/cc-88x31.png | Bin 5460 -> 0 bytes
forum/skins/default/media/images/cc-wiki.png | Bin 2333 -> 0 bytes
.../default/media/images/close-small-dark.png | Bin 226 -> 0 bytes
.../media/images/close-small-hover.png | Bin 337 -> 0 bytes
.../default/media/images/close-small.png | Bin 293 -> 0 bytes
forum/skins/default/media/images/dash.gif | Bin 44 -> 0 bytes
.../media/images/djangomade124x25_grey.gif | Bin 2035 -> 0 bytes
forum/skins/default/media/images/dot-g.gif | Bin 61 -> 0 bytes
forum/skins/default/media/images/dot-list.gif | Bin 56 -> 0 bytes
forum/skins/default/media/images/edit.png | Bin 758 -> 0 bytes
.../media/images/expander-arrow-hide.gif | Bin 126 -> 0 bytes
.../media/images/expander-arrow-show.gif | Bin 135 -> 0 bytes
forum/skins/default/media/images/favicon.gif | Bin 3918 -> 0 bytes
.../default/media/images/feed-icon-small.png | Bin 689 -> 0 bytes
.../media/images/gray-up-arrow-h18px.png | Bin 383 -> 0 bytes
forum/skins/default/media/images/grippie.png | Bin 162 -> 0 bytes
.../skins/default/media/images/indicator.gif | Bin 2545 -> 0 bytes
forum/skins/default/media/images/logo.gif | Bin 2114 -> 0 bytes
forum/skins/default/media/images/logo.png | Bin 2081 -> 0 bytes
forum/skins/default/media/images/logo1.png | Bin 2752 -> 0 bytes
forum/skins/default/media/images/logo2.png | Bin 2124 -> 0 bytes
forum/skins/default/media/images/medala.gif | Bin 801 -> 0 bytes
.../skins/default/media/images/medala_on.gif | Bin 957 -> 0 bytes
forum/skins/default/media/images/new.gif | Bin 635 -> 0 bytes
forum/skins/default/media/images/nophoto.png | Bin 696 -> 0 bytes
forum/skins/default/media/images/openid.gif | Bin 910 -> 0 bytes
.../skins/default/media/images/openid/aol.gif | Bin 2205 -> 0 bytes
.../default/media/images/openid/blogger.ico | Bin 3638 -> 0 bytes
.../default/media/images/openid/claimid.ico | Bin 3638 -> 0 bytes
.../default/media/images/openid/facebook.gif | Bin 2075 -> 0 bytes
.../default/media/images/openid/flickr.ico | Bin 1150 -> 0 bytes
.../default/media/images/openid/google.gif | Bin 1596 -> 0 bytes
.../media/images/openid/livejournal.ico | Bin 5222 -> 0 bytes
.../default/media/images/openid/myopenid.ico | Bin 2862 -> 0 bytes
.../media/images/openid/openid-inputicon.gif | Bin 237 -> 0 bytes
.../default/media/images/openid/openid.gif | Bin 740 -> 0 bytes
.../media/images/openid/technorati.ico | Bin 2294 -> 0 bytes
.../default/media/images/openid/twitter.png | Bin 3130 -> 0 bytes
.../default/media/images/openid/verisign.ico | Bin 4710 -> 0 bytes
.../default/media/images/openid/vidoop.ico | Bin 1406 -> 0 bytes
.../default/media/images/openid/wordpress.ico | Bin 1150 -> 0 bytes
.../default/media/images/openid/yahoo.gif | Bin 1510 -> 0 bytes
forum/skins/default/media/images/quest-bg.gif | Bin 294 -> 0 bytes
.../default/media/images/vote-accepted-on.png | Bin 1124 -> 0 bytes
.../default/media/images/vote-accepted.png | Bin 1058 -> 0 bytes
.../media/images/vote-arrow-down-on.png | Bin 905 -> 0 bytes
.../default/media/images/vote-arrow-down.png | Bin 876 -> 0 bytes
.../default/media/images/vote-arrow-up-on.png | Bin 906 -> 0 bytes
.../default/media/images/vote-arrow-up.png | Bin 843 -> 0 bytes
.../media/images/vote-favorite-off.png | Bin 930 -> 0 bytes
.../default/media/images/vote-favorite-on.png | Bin 1023 -> 0 bytes
.../media/jquery-openid/images/aol.gif | Bin 2205 -> 0 bytes
.../media/jquery-openid/images/blogger-1.png | Bin 432 -> 0 bytes
.../media/jquery-openid/images/blogger.ico | Bin 3638 -> 0 bytes
.../media/jquery-openid/images/claimid-0.png | Bin 629 -> 0 bytes
.../media/jquery-openid/images/claimid.ico | Bin 3638 -> 0 bytes
.../media/jquery-openid/images/facebook.gif | Bin 2075 -> 0 bytes
.../media/jquery-openid/images/flickr.ico | Bin 1150 -> 0 bytes
.../media/jquery-openid/images/flickr.png | Bin 426 -> 0 bytes
.../media/jquery-openid/images/google.gif | Bin 1596 -> 0 bytes
.../jquery-openid/images/livejournal-1.png | Bin 713 -> 0 bytes
.../jquery-openid/images/livejournal.ico | Bin 5222 -> 0 bytes
.../media/jquery-openid/images/myopenid-2.png | Bin 511 -> 0 bytes
.../media/jquery-openid/images/myopenid.ico | Bin 2862 -> 0 bytes
.../jquery-openid/images/openid-inputicon.gif | Bin 237 -> 0 bytes
.../media/jquery-openid/images/openid.gif | Bin 740 -> 0 bytes
.../media/jquery-openid/images/openidico.png | Bin 654 -> 0 bytes
.../jquery-openid/images/openidico16.png | Bin 554 -> 0 bytes
.../jquery-openid/images/technorati-1.png | Bin 606 -> 0 bytes
.../media/jquery-openid/images/technorati.ico | Bin 2294 -> 0 bytes
.../media/jquery-openid/images/verisign-2.png | Bin 859 -> 0 bytes
.../media/jquery-openid/images/verisign.ico | Bin 4710 -> 0 bytes
.../media/jquery-openid/images/vidoop.ico | Bin 1406 -> 0 bytes
.../media/jquery-openid/images/vidoop.png | Bin 499 -> 0 bytes
.../media/jquery-openid/images/wordpress.ico | Bin 1150 -> 0 bytes
.../media/jquery-openid/images/wordpress.png | Bin 566 -> 0 bytes
.../media/jquery-openid/images/yahoo.gif | Bin 1682 -> 0 bytes
.../media/jquery-openid/jquery.openid.js | 111 -
.../default/media/jquery-openid/openid.css | 75 -
.../default/media/js/com.cnprog.admin.js | 13 -
.../default/media/js/com.cnprog.editor.js | 68 -
.../skins/default/media/js/com.cnprog.i18n.js | 159 -
.../skins/default/media/js/com.cnprog.post.js | 691 ----
.../media/js/com.cnprog.tag_selector.js | 171 -
.../default/media/js/com.cnprog.utils.js | 132 -
forum/skins/default/media/js/compress.bat | 5 -
forum/skins/default/media/js/excanvas.pack.js | 1 -
forum/skins/default/media/js/flot-build.bat | 3 -
forum/skins/default/media/js/jquery-1.2.6.js | 3549 -----------------
.../default/media/js/jquery-1.2.6.min.js | 32 -
.../default/media/js/jquery.ajaxfileupload.js | 195 -
forum/skins/default/media/js/jquery.flot.js | 2421 -----------
.../default/media/js/jquery.flot.pack.js | 1 -
forum/skins/default/media/js/jquery.form.js | 654 ---
forum/skins/default/media/js/jquery.i18n.js | 133 -
forum/skins/default/media/js/jquery.openid.js | 176 -
.../default/media/js/jquery.validate.pack.js | 15 -
forum/skins/default/media/js/se_hilite.js | 1 -
forum/skins/default/media/js/se_hilite_src.js | 273 --
.../media/js/wmd/images/wmd-buttons.png | Bin 7465 -> 0 bytes
.../default/media/js/wmd/showdown-min.js | 1 -
forum/skins/default/media/js/wmd/showdown.js | 1309 ------
forum/skins/default/media/js/wmd/wmd-min.js | 1 -
.../skins/default/media/js/wmd/wmd-test.html | 158 -
forum/skins/default/media/js/wmd/wmd.css | 129 -
forum/skins/default/media/js/wmd/wmd.js | 2388 -----------
.../default/media/js/yuicompressor-2.4.2.jar | Bin 851219 -> 0 bytes
forum/skins/default/media/style/auth.css | 48 -
forum/skins/default/media/style/default.css | 1754 --------
.../media/style/jquery.autocomplete.css | 49 -
forum/skins/default/media/style/openid.css | 45 -
forum/skins/default/media/style/prettify.css | 27 -
forum/skins/default/media/style/style.css | 2459 ------------
forum/skins/default/templates/404.html | 49 -
forum/skins/default/templates/500.html | 35 -
forum/skins/default/templates/about.html | 36 -
.../default/templates/account_settings.html | 45 -
.../skins/default/templates/answer_edit.html | 85 -
.../default/templates/answer_edit_tips.html | 55 -
forum/skins/default/templates/ask.html | 134 -
.../default/templates/auth/complete.html | 95 -
.../skins/default/templates/auth/signin.html | 161 -
.../skins/default/templates/auth/signup.html | 32 -
forum/skins/default/templates/badge.html | 37 -
forum/skins/default/templates/badges.html | 76 -
forum/skins/default/templates/base.html | 83 -
.../skins/default/templates/base_content.html | 74 -
forum/skins/default/templates/book.html | 152 -
forum/skins/default/templates/changepw.html | 18 -
forum/skins/default/templates/close.html | 36 -
.../templates/edit_user_email_feeds_form.html | 4 -
forum/skins/default/templates/faq.html | 146 -
.../templates/fbconnect/xd_receiver.html | 10 -
forum/skins/default/templates/feedback.html | 55 -
.../default/templates/feedback_email.txt | 19 -
.../templates/feeds/rss_description.html | 1 -
.../default/templates/feeds/rss_title.html | 1 -
forum/skins/default/templates/footer.html | 48 -
forum/skins/default/templates/header.html | 65 -
forum/skins/default/templates/index.html | 124 -
forum/skins/default/templates/logout.html | 23 -
forum/skins/default/templates/notarobot.html | 15 -
forum/skins/default/templates/pagesize.html | 27 -
forum/skins/default/templates/paginator.html | 38 -
.../templates/post_contributor_info.html | 55 -
forum/skins/default/templates/privacy.html | 42 -
forum/skins/default/templates/question.html | 508 ---
.../default/templates/question_edit.html | 131 -
.../default/templates/question_edit_tips.html | 53 -
.../default/templates/question_retag.html | 106 -
.../templates/question_summary_list_roll.html | 55 -
forum/skins/default/templates/questions.html | 235 --
forum/skins/default/templates/reopen.html | 37 -
.../default/templates/revisions_answer.html | 83 -
.../default/templates/revisions_question.html | 83 -
.../skins/default/templates/tag_selector.html | 42 -
forum/skins/default/templates/tags.html | 67 -
forum/skins/default/templates/user.html | 39 -
forum/skins/default/templates/user_edit.html | 95 -
.../templates/user_email_subscriptions.html | 26 -
.../default/templates/user_favorites.html | 8 -
.../skins/default/templates/user_footer.html | 4 -
forum/skins/default/templates/user_info.html | 116 -
.../skins/default/templates/user_recent.html | 26 -
.../default/templates/user_reputation.html | 42 -
.../default/templates/user_responses.html | 23 -
forum/skins/default/templates/user_stats.html | 138 -
forum/skins/default/templates/user_tabs.html | 32 -
forum/skins/default/templates/user_votes.html | 32 -
forum/skins/default/templates/users.html | 73 -
.../default/templates/users_questions.html | 66 -
forum/templatetags/__init__.py | 0
forum/templatetags/extra_filters.py | 98 -
forum/templatetags/extra_tags.py | 357 --
forum/templatetags/smart_if.py | 401 --
forum/upfiles/README | 2 -
forum/urls.py | 114 -
forum/user_messages/__init__.py | 36 -
forum/user_messages/context_processors.py | 52 -
forum/utils/__init__.py | 0
forum/utils/cache.py | 92 -
forum/utils/decorators.py | 25 -
forum/utils/diff.py | 66 -
forum/utils/forms.py | 151 -
forum/utils/html.py | 51 -
forum/utils/lists.py | 86 -
forum/utils/odict.py | 1399 -------
forum/views/README | 12 -
forum/views/__init__.py | 6 -
forum/views/auth.py | 212 -
forum/views/commands.py | 335 --
forum/views/meta.py | 91 -
forum/views/readers.py | 588 ---
forum/views/users.py | 1009 -----
forum/views/writers.py | 442 --
forum_modules/__init__.py | 0
forum_modules/books/__init__.py | 3 -
forum_modules/books/models.py | 63 -
forum_modules/books/urls.py | 10 -
forum_modules/books/views.py | 142 -
forum_modules/facebookauth/__init__.py | 0
forum_modules/facebookauth/authentication.py | 85 -
forum_modules/facebookauth/settings.py | 3 -
.../facebookauth/templates/button.html | 38 -
.../facebookauth/templates/xd_receiver.html | 1 -
forum_modules/facebookauth/urls.py | 9 -
forum_modules/facebookauth/views.py | 11 -
forum_modules/localauth/__init__.py | 0
forum_modules/localauth/authentication.py | 18 -
forum_modules/localauth/forms.py | 77 -
.../localauth/templates/loginform.html | 31 -
forum_modules/localauth/urls.py | 8 -
forum_modules/localauth/views.py | 30 -
forum_modules/oauthauth/__init__.py | 0
forum_modules/oauthauth/authentication.py | 41 -
forum_modules/oauthauth/consumer.py | 87 -
forum_modules/oauthauth/lib/__init__.py | 0
forum_modules/oauthauth/lib/oauth.py | 594 ---
forum_modules/oauthauth/settings.py | 3 -
forum_modules/openidauth/__init__.py | 0
forum_modules/openidauth/authentication.py | 196 -
forum_modules/openidauth/consumer.py | 112 -
forum_modules/openidauth/models.py | 26 -
forum_modules/openidauth/settings.py | 9 -
forum_modules/openidauth/store.py | 79 -
.../openidauth/templates/openidurl.html | 20 -
forum_modules/pgfulltext/DISABLED | 0
forum_modules/pgfulltext/__init__.py | 9 -
forum_modules/pgfulltext/handlers.py | 11 -
forum_modules/pgfulltext/management.py | 29 -
forum_modules/pgfulltext/pg_fts_install.sql | 38 -
forum_modules/sphinxfulltext/DISABLED | 0
forum_modules/sphinxfulltext/__init__.py | 0
forum_modules/sphinxfulltext/dependencies.py | 2 -
forum_modules/sphinxfulltext/handlers.py | 4 -
forum_modules/sphinxfulltext/models.py | 10 -
forum_modules/sphinxfulltext/settings.py | 5 -
locale/en/LC_MESSAGES/django.po | 3496 ----------------
locale/es/LC_MESSAGES/django.mo | Bin 49713 -> 0 bytes
locale/es/LC_MESSAGES/django.po | 2695 -------------
locale/zh_CN/LC_MESSAGES/django.mo | Bin 37880 -> 0 bytes
locale/zh_CN/LC_MESSAGES/django.po | 2418 -----------
manage.py | 11 -
osqa.iml | 19 -
osqa.wsgi.dist | 7 -
rmpyc | 1 -
settings.py | 101 -
settings_local.py.dist | 110 -
sphinx/sphinx.conf | 127 -
sql_scripts/091111_upgrade_evgeny.sql | 1 -
sql_scripts/091208_upgrade_evgeny.sql | 1 -
sql_scripts/091208_upgrade_evgeny_1.sql | 1 -
sql_scripts/100108_upgrade_ef.sql | 4 -
sql_scripts/badges.sql | 37 -
sql_scripts/cnprog.xml | 1498 -------
sql_scripts/cnprog_new_install.sql | 811 ----
sql_scripts/cnprog_new_install_2009_02_28.sql | 456 ---
sql_scripts/cnprog_new_install_2009_03_31.sql | 891 -----
sql_scripts/cnprog_new_install_2009_04_07.sql | 24 -
sql_scripts/cnprog_new_install_2009_04_09.sql | 904 -----
sql_scripts/drop-all-tables.sh | 4 -
sql_scripts/drop-auth.sql | 8 -
sql_scripts/pg_fts_install.sql | 38 -
sql_scripts/update_2009_01_13_001.sql | 62 -
sql_scripts/update_2009_01_13_002.sql | 1 -
sql_scripts/update_2009_01_18_001.sql | 62 -
sql_scripts/update_2009_01_24.sql | 2 -
sql_scripts/update_2009_01_25_001.sql | 2 -
sql_scripts/update_2009_02_26_001.sql | 19 -
sql_scripts/update_2009_04_10_001.sql | 3 -
sql_scripts/update_2009_07_05_EF.sql | 3 -
sql_scripts/update_2009_12_24_001.sql | 5 -
sql_scripts/update_2009_12_27_001.sql | 3 -
sql_scripts/update_2009_12_27_002.sql | 1 -
sql_scripts/update_2010_01_23.sql | 9 -
sql_scripts/update_2010_02_22.sql | 1 -
urls.py | 11 -
328 files changed, 46815 deletions(-)
delete mode 100644 .project
delete mode 100644 .pydevproject
delete mode 100644 HOW_TO_DEBUG
delete mode 100644 INSTALL
delete mode 100644 PENDING
delete mode 100644 README
delete mode 100644 ROADMAP.rst
delete mode 100644 WISH_LIST
delete mode 100644 __init__.py
delete mode 100644 context.py
delete mode 100644 cron/send_email_alerts
delete mode 100644 dos2unix.sh
delete mode 100644 forum/__init__.py
delete mode 100644 forum/admin.py
delete mode 100644 forum/auth.py
delete mode 100755 forum/authentication/__init__.py
delete mode 100755 forum/authentication/base.py
delete mode 100755 forum/authentication/forms.py
delete mode 100644 forum/const.py
delete mode 100644 forum/feed.py
delete mode 100644 forum/forms.py
delete mode 100644 forum/management/__init__.py
delete mode 100644 forum/management/commands/__init__.py
delete mode 100644 forum/management/commands/base_command.py
delete mode 100644 forum/management/commands/clean_award_badges.py
delete mode 100644 forum/management/commands/message_to_everyone.py
delete mode 100644 forum/management/commands/multi_award_badges.py
delete mode 100644 forum/management/commands/once_award_badges.py
delete mode 100644 forum/management/commands/sample_command.py
delete mode 100644 forum/management/commands/send_email_alerts.py
delete mode 100644 forum/management/commands/subscribe_everyone.py
delete mode 100644 forum/middleware/__init__.py
delete mode 100644 forum/middleware/anon_user.py
delete mode 100644 forum/middleware/cancel.py
delete mode 100644 forum/middleware/pagesize.py
delete mode 100755 forum/models/__init__.py
delete mode 100755 forum/models/answer.py
delete mode 100755 forum/models/base.py
delete mode 100755 forum/models/meta.py
delete mode 100755 forum/models/question.py
delete mode 100755 forum/models/repute.py
delete mode 100755 forum/models/tag.py
delete mode 100755 forum/models/user.py
delete mode 100755 forum/modules.py
delete mode 100644 forum/sitemap.py
delete mode 100644 forum/skins/README
delete mode 100644 forum/skins/__init__.py
delete mode 100644 forum/skins/common/media/README
delete mode 100644 forum/skins/default/media/images/blue-up-arrow-h18px.png
delete mode 100644 forum/skins/default/media/images/box-arrow.gif
delete mode 100644 forum/skins/default/media/images/bullet_green.gif
delete mode 100644 forum/skins/default/media/images/cc-88x31.png
delete mode 100644 forum/skins/default/media/images/cc-wiki.png
delete mode 100644 forum/skins/default/media/images/close-small-dark.png
delete mode 100644 forum/skins/default/media/images/close-small-hover.png
delete mode 100644 forum/skins/default/media/images/close-small.png
delete mode 100644 forum/skins/default/media/images/dash.gif
delete mode 100644 forum/skins/default/media/images/djangomade124x25_grey.gif
delete mode 100644 forum/skins/default/media/images/dot-g.gif
delete mode 100644 forum/skins/default/media/images/dot-list.gif
delete mode 100644 forum/skins/default/media/images/edit.png
delete mode 100644 forum/skins/default/media/images/expander-arrow-hide.gif
delete mode 100644 forum/skins/default/media/images/expander-arrow-show.gif
delete mode 100644 forum/skins/default/media/images/favicon.gif
delete mode 100644 forum/skins/default/media/images/feed-icon-small.png
delete mode 100644 forum/skins/default/media/images/gray-up-arrow-h18px.png
delete mode 100644 forum/skins/default/media/images/grippie.png
delete mode 100644 forum/skins/default/media/images/indicator.gif
delete mode 100644 forum/skins/default/media/images/logo.gif
delete mode 100644 forum/skins/default/media/images/logo.png
delete mode 100644 forum/skins/default/media/images/logo1.png
delete mode 100644 forum/skins/default/media/images/logo2.png
delete mode 100644 forum/skins/default/media/images/medala.gif
delete mode 100644 forum/skins/default/media/images/medala_on.gif
delete mode 100644 forum/skins/default/media/images/new.gif
delete mode 100644 forum/skins/default/media/images/nophoto.png
delete mode 100644 forum/skins/default/media/images/openid.gif
delete mode 100644 forum/skins/default/media/images/openid/aol.gif
delete mode 100644 forum/skins/default/media/images/openid/blogger.ico
delete mode 100644 forum/skins/default/media/images/openid/claimid.ico
delete mode 100644 forum/skins/default/media/images/openid/facebook.gif
delete mode 100644 forum/skins/default/media/images/openid/flickr.ico
delete mode 100644 forum/skins/default/media/images/openid/google.gif
delete mode 100644 forum/skins/default/media/images/openid/livejournal.ico
delete mode 100644 forum/skins/default/media/images/openid/myopenid.ico
delete mode 100644 forum/skins/default/media/images/openid/openid-inputicon.gif
delete mode 100644 forum/skins/default/media/images/openid/openid.gif
delete mode 100644 forum/skins/default/media/images/openid/technorati.ico
delete mode 100755 forum/skins/default/media/images/openid/twitter.png
delete mode 100644 forum/skins/default/media/images/openid/verisign.ico
delete mode 100644 forum/skins/default/media/images/openid/vidoop.ico
delete mode 100644 forum/skins/default/media/images/openid/wordpress.ico
delete mode 100644 forum/skins/default/media/images/openid/yahoo.gif
delete mode 100644 forum/skins/default/media/images/quest-bg.gif
delete mode 100644 forum/skins/default/media/images/vote-accepted-on.png
delete mode 100644 forum/skins/default/media/images/vote-accepted.png
delete mode 100644 forum/skins/default/media/images/vote-arrow-down-on.png
delete mode 100644 forum/skins/default/media/images/vote-arrow-down.png
delete mode 100644 forum/skins/default/media/images/vote-arrow-up-on.png
delete mode 100644 forum/skins/default/media/images/vote-arrow-up.png
delete mode 100644 forum/skins/default/media/images/vote-favorite-off.png
delete mode 100644 forum/skins/default/media/images/vote-favorite-on.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/aol.gif
delete mode 100644 forum/skins/default/media/jquery-openid/images/blogger-1.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/blogger.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/claimid-0.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/claimid.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/facebook.gif
delete mode 100644 forum/skins/default/media/jquery-openid/images/flickr.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/flickr.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/google.gif
delete mode 100644 forum/skins/default/media/jquery-openid/images/livejournal-1.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/livejournal.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/myopenid-2.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/myopenid.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/openid-inputicon.gif
delete mode 100644 forum/skins/default/media/jquery-openid/images/openid.gif
delete mode 100644 forum/skins/default/media/jquery-openid/images/openidico.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/openidico16.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/technorati-1.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/technorati.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/verisign-2.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/verisign.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/vidoop.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/vidoop.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/wordpress.ico
delete mode 100644 forum/skins/default/media/jquery-openid/images/wordpress.png
delete mode 100644 forum/skins/default/media/jquery-openid/images/yahoo.gif
delete mode 100644 forum/skins/default/media/jquery-openid/jquery.openid.js
delete mode 100644 forum/skins/default/media/jquery-openid/openid.css
delete mode 100644 forum/skins/default/media/js/com.cnprog.admin.js
delete mode 100644 forum/skins/default/media/js/com.cnprog.editor.js
delete mode 100644 forum/skins/default/media/js/com.cnprog.i18n.js
delete mode 100644 forum/skins/default/media/js/com.cnprog.post.js
delete mode 100644 forum/skins/default/media/js/com.cnprog.tag_selector.js
delete mode 100644 forum/skins/default/media/js/com.cnprog.utils.js
delete mode 100644 forum/skins/default/media/js/compress.bat
delete mode 100644 forum/skins/default/media/js/excanvas.pack.js
delete mode 100644 forum/skins/default/media/js/flot-build.bat
delete mode 100644 forum/skins/default/media/js/jquery-1.2.6.js
delete mode 100644 forum/skins/default/media/js/jquery-1.2.6.min.js
delete mode 100644 forum/skins/default/media/js/jquery.ajaxfileupload.js
delete mode 100644 forum/skins/default/media/js/jquery.flot.js
delete mode 100644 forum/skins/default/media/js/jquery.flot.pack.js
delete mode 100644 forum/skins/default/media/js/jquery.form.js
delete mode 100644 forum/skins/default/media/js/jquery.i18n.js
delete mode 100644 forum/skins/default/media/js/jquery.openid.js
delete mode 100644 forum/skins/default/media/js/jquery.validate.pack.js
delete mode 100644 forum/skins/default/media/js/se_hilite.js
delete mode 100644 forum/skins/default/media/js/se_hilite_src.js
delete mode 100644 forum/skins/default/media/js/wmd/images/wmd-buttons.png
delete mode 100644 forum/skins/default/media/js/wmd/showdown-min.js
delete mode 100644 forum/skins/default/media/js/wmd/showdown.js
delete mode 100644 forum/skins/default/media/js/wmd/wmd-min.js
delete mode 100644 forum/skins/default/media/js/wmd/wmd-test.html
delete mode 100644 forum/skins/default/media/js/wmd/wmd.css
delete mode 100644 forum/skins/default/media/js/wmd/wmd.js
delete mode 100644 forum/skins/default/media/js/yuicompressor-2.4.2.jar
delete mode 100755 forum/skins/default/media/style/auth.css
delete mode 100644 forum/skins/default/media/style/default.css
delete mode 100644 forum/skins/default/media/style/jquery.autocomplete.css
delete mode 100644 forum/skins/default/media/style/openid.css
delete mode 100644 forum/skins/default/media/style/prettify.css
delete mode 100644 forum/skins/default/media/style/style.css
delete mode 100644 forum/skins/default/templates/404.html
delete mode 100644 forum/skins/default/templates/500.html
delete mode 100644 forum/skins/default/templates/about.html
delete mode 100755 forum/skins/default/templates/account_settings.html
delete mode 100644 forum/skins/default/templates/answer_edit.html
delete mode 100644 forum/skins/default/templates/answer_edit_tips.html
delete mode 100644 forum/skins/default/templates/ask.html
delete mode 100755 forum/skins/default/templates/auth/complete.html
delete mode 100755 forum/skins/default/templates/auth/signin.html
delete mode 100755 forum/skins/default/templates/auth/signup.html
delete mode 100644 forum/skins/default/templates/badge.html
delete mode 100644 forum/skins/default/templates/badges.html
delete mode 100755 forum/skins/default/templates/base.html
delete mode 100644 forum/skins/default/templates/base_content.html
delete mode 100644 forum/skins/default/templates/book.html
delete mode 100755 forum/skins/default/templates/changepw.html
delete mode 100644 forum/skins/default/templates/close.html
delete mode 100644 forum/skins/default/templates/edit_user_email_feeds_form.html
delete mode 100644 forum/skins/default/templates/faq.html
delete mode 100755 forum/skins/default/templates/fbconnect/xd_receiver.html
delete mode 100644 forum/skins/default/templates/feedback.html
delete mode 100644 forum/skins/default/templates/feedback_email.txt
delete mode 100644 forum/skins/default/templates/feeds/rss_description.html
delete mode 100644 forum/skins/default/templates/feeds/rss_title.html
delete mode 100644 forum/skins/default/templates/footer.html
delete mode 100644 forum/skins/default/templates/header.html
delete mode 100755 forum/skins/default/templates/index.html
delete mode 100644 forum/skins/default/templates/logout.html
delete mode 100644 forum/skins/default/templates/notarobot.html
delete mode 100644 forum/skins/default/templates/pagesize.html
delete mode 100644 forum/skins/default/templates/paginator.html
delete mode 100644 forum/skins/default/templates/post_contributor_info.html
delete mode 100644 forum/skins/default/templates/privacy.html
delete mode 100644 forum/skins/default/templates/question.html
delete mode 100644 forum/skins/default/templates/question_edit.html
delete mode 100644 forum/skins/default/templates/question_edit_tips.html
delete mode 100644 forum/skins/default/templates/question_retag.html
delete mode 100644 forum/skins/default/templates/question_summary_list_roll.html
delete mode 100644 forum/skins/default/templates/questions.html
delete mode 100644 forum/skins/default/templates/reopen.html
delete mode 100644 forum/skins/default/templates/revisions_answer.html
delete mode 100644 forum/skins/default/templates/revisions_question.html
delete mode 100644 forum/skins/default/templates/tag_selector.html
delete mode 100644 forum/skins/default/templates/tags.html
delete mode 100644 forum/skins/default/templates/user.html
delete mode 100644 forum/skins/default/templates/user_edit.html
delete mode 100644 forum/skins/default/templates/user_email_subscriptions.html
delete mode 100644 forum/skins/default/templates/user_favorites.html
delete mode 100644 forum/skins/default/templates/user_footer.html
delete mode 100644 forum/skins/default/templates/user_info.html
delete mode 100644 forum/skins/default/templates/user_recent.html
delete mode 100644 forum/skins/default/templates/user_reputation.html
delete mode 100644 forum/skins/default/templates/user_responses.html
delete mode 100644 forum/skins/default/templates/user_stats.html
delete mode 100644 forum/skins/default/templates/user_tabs.html
delete mode 100644 forum/skins/default/templates/user_votes.html
delete mode 100644 forum/skins/default/templates/users.html
delete mode 100644 forum/skins/default/templates/users_questions.html
delete mode 100644 forum/templatetags/__init__.py
delete mode 100644 forum/templatetags/extra_filters.py
delete mode 100644 forum/templatetags/extra_tags.py
delete mode 100644 forum/templatetags/smart_if.py
delete mode 100644 forum/upfiles/README
delete mode 100644 forum/urls.py
delete mode 100644 forum/user_messages/__init__.py
delete mode 100644 forum/user_messages/context_processors.py
delete mode 100644 forum/utils/__init__.py
delete mode 100644 forum/utils/cache.py
delete mode 100644 forum/utils/decorators.py
delete mode 100644 forum/utils/diff.py
delete mode 100644 forum/utils/forms.py
delete mode 100644 forum/utils/html.py
delete mode 100644 forum/utils/lists.py
delete mode 100644 forum/utils/odict.py
delete mode 100644 forum/views/README
delete mode 100644 forum/views/__init__.py
delete mode 100755 forum/views/auth.py
delete mode 100644 forum/views/commands.py
delete mode 100644 forum/views/meta.py
delete mode 100644 forum/views/readers.py
delete mode 100644 forum/views/users.py
delete mode 100644 forum/views/writers.py
delete mode 100755 forum_modules/__init__.py
delete mode 100755 forum_modules/books/__init__.py
delete mode 100755 forum_modules/books/models.py
delete mode 100755 forum_modules/books/urls.py
delete mode 100755 forum_modules/books/views.py
delete mode 100755 forum_modules/facebookauth/__init__.py
delete mode 100755 forum_modules/facebookauth/authentication.py
delete mode 100755 forum_modules/facebookauth/settings.py
delete mode 100755 forum_modules/facebookauth/templates/button.html
delete mode 100755 forum_modules/facebookauth/templates/xd_receiver.html
delete mode 100755 forum_modules/facebookauth/urls.py
delete mode 100755 forum_modules/facebookauth/views.py
delete mode 100755 forum_modules/localauth/__init__.py
delete mode 100755 forum_modules/localauth/authentication.py
delete mode 100755 forum_modules/localauth/forms.py
delete mode 100755 forum_modules/localauth/templates/loginform.html
delete mode 100755 forum_modules/localauth/urls.py
delete mode 100755 forum_modules/localauth/views.py
delete mode 100755 forum_modules/oauthauth/__init__.py
delete mode 100755 forum_modules/oauthauth/authentication.py
delete mode 100755 forum_modules/oauthauth/consumer.py
delete mode 100755 forum_modules/oauthauth/lib/__init__.py
delete mode 100755 forum_modules/oauthauth/lib/oauth.py
delete mode 100755 forum_modules/oauthauth/settings.py
delete mode 100755 forum_modules/openidauth/__init__.py
delete mode 100755 forum_modules/openidauth/authentication.py
delete mode 100755 forum_modules/openidauth/consumer.py
delete mode 100755 forum_modules/openidauth/models.py
delete mode 100755 forum_modules/openidauth/settings.py
delete mode 100755 forum_modules/openidauth/store.py
delete mode 100755 forum_modules/openidauth/templates/openidurl.html
delete mode 100755 forum_modules/pgfulltext/DISABLED
delete mode 100755 forum_modules/pgfulltext/__init__.py
delete mode 100755 forum_modules/pgfulltext/handlers.py
delete mode 100755 forum_modules/pgfulltext/management.py
delete mode 100755 forum_modules/pgfulltext/pg_fts_install.sql
delete mode 100755 forum_modules/sphinxfulltext/DISABLED
delete mode 100755 forum_modules/sphinxfulltext/__init__.py
delete mode 100755 forum_modules/sphinxfulltext/dependencies.py
delete mode 100755 forum_modules/sphinxfulltext/handlers.py
delete mode 100755 forum_modules/sphinxfulltext/models.py
delete mode 100755 forum_modules/sphinxfulltext/settings.py
delete mode 100644 locale/en/LC_MESSAGES/django.po
delete mode 100644 locale/es/LC_MESSAGES/django.mo
delete mode 100644 locale/es/LC_MESSAGES/django.po
delete mode 100644 locale/zh_CN/LC_MESSAGES/django.mo
delete mode 100644 locale/zh_CN/LC_MESSAGES/django.po
delete mode 100644 manage.py
delete mode 100755 osqa.iml
delete mode 100644 osqa.wsgi.dist
delete mode 100755 rmpyc
delete mode 100755 settings.py
delete mode 100755 settings_local.py.dist
delete mode 100644 sphinx/sphinx.conf
delete mode 100644 sql_scripts/091111_upgrade_evgeny.sql
delete mode 100644 sql_scripts/091208_upgrade_evgeny.sql
delete mode 100644 sql_scripts/091208_upgrade_evgeny_1.sql
delete mode 100644 sql_scripts/100108_upgrade_ef.sql
delete mode 100644 sql_scripts/badges.sql
delete mode 100644 sql_scripts/cnprog.xml
delete mode 100644 sql_scripts/cnprog_new_install.sql
delete mode 100644 sql_scripts/cnprog_new_install_2009_02_28.sql
delete mode 100644 sql_scripts/cnprog_new_install_2009_03_31.sql
delete mode 100644 sql_scripts/cnprog_new_install_2009_04_07.sql
delete mode 100644 sql_scripts/cnprog_new_install_2009_04_09.sql
delete mode 100644 sql_scripts/drop-all-tables.sh
delete mode 100644 sql_scripts/drop-auth.sql
delete mode 100644 sql_scripts/pg_fts_install.sql
delete mode 100644 sql_scripts/update_2009_01_13_001.sql
delete mode 100644 sql_scripts/update_2009_01_13_002.sql
delete mode 100644 sql_scripts/update_2009_01_18_001.sql
delete mode 100644 sql_scripts/update_2009_01_24.sql
delete mode 100644 sql_scripts/update_2009_01_25_001.sql
delete mode 100644 sql_scripts/update_2009_02_26_001.sql
delete mode 100644 sql_scripts/update_2009_04_10_001.sql
delete mode 100644 sql_scripts/update_2009_07_05_EF.sql
delete mode 100644 sql_scripts/update_2009_12_24_001.sql
delete mode 100644 sql_scripts/update_2009_12_27_001.sql
delete mode 100644 sql_scripts/update_2009_12_27_002.sql
delete mode 100755 sql_scripts/update_2010_01_23.sql
delete mode 100644 sql_scripts/update_2010_02_22.sql
delete mode 100644 urls.py
diff --git a/.project b/.project
deleted file mode 100644
index 8e56b00..0000000
--- a/.project
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
- osqa
-
-
-
-
-
- org.eclipse.wst.jsdt.core.javascriptValidator
-
-
-
-
- org.python.pydev.PyDevBuilder
-
-
-
-
-
- org.python.pydev.pythonNature
- org.eclipse.wst.jsdt.core.jsNature
-
-
diff --git a/.pydevproject b/.pydevproject
deleted file mode 100644
index f7f3fd1..0000000
--- a/.pydevproject
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-Default
-python 2.6
-
-/osqa
-
-
diff --git a/HOW_TO_DEBUG b/HOW_TO_DEBUG
deleted file mode 100644
index ba36198..0000000
--- a/HOW_TO_DEBUG
+++ /dev/null
@@ -1,39 +0,0 @@
-1) LOGGING
-Please remember that log files may contain plaintext passwords, etc.
-
-Please do not add print statements - at least do not commit them to git
-because in some environments printing to stdout causes errors
-
-Instead use python logging this way:
---------------------------------
-#somewere on top of file
-import logging
-
-#anywhere below
-logging.debug('this maybe works')
-logging.error('have big error!')
-#or even
-logging.debug('') #this will add time, line number, function and file record
-#sometimes useful record for call tracing on its own
-#etc - take a look at http://docs.python.org/library/logging.html
--------------------------------
-
-in OSQA logging is currently set up in settings_local.py.dist
-please update it if you need - in older revs logging strings have less info
-
-messages of interest can be grepped out of the log file by module/file/function name
-e.g. to take out all django_authopenid logs run:
->grep 'osqa\/django_authopenid' log/django.osqa.log | sed 's/^.*MSG: //'
-in the example above 'sed' call truncates out a long prefix
-and makes output look more meaningful
-
-2) DJANGO DEBUG TOOLBAR
-osqa works with django debug toolbar
-if debugging under apache server, check
-that debug toolbar media is loaded correctly
-if toolbar is enabled but you do not see it, possibly some Alias statement
-in apache config is wrong in your VirtualHost or elsewhere
-
-3) If you discover new debugging techniques, please add here.
-Possible areas to improve - at this point there is no SQL query logging,
-as well as request data and http header.
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index f70b3ec..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,314 +0,0 @@
-CONTENTS
-------------------
-A. PREREQUISITES
-B. INSTALLATION
- 1. Settings file
- 2. Database
- 3. Running OSQA in the development server
- 4. Installation under Apache/WSGI
- 5. Full text search
- 6. Email subscriptions
- 7. Sitemap
- 8. Miscellaneous
-C. CONFIGURATION PARAMETERS (settings_local.py)
-D. CUSTOMIZATION
-
-
-A. PREREQUISITES
------------------------------------------------
-0. We recommend you to use python-setuptools to install pre-requirement libraries.
-If you haven't installed it, please try to install it first.
-e.g, sudo apt-get install python-setuptools
-
-1. Python2.5/2.6, MySQL, Django v1.0/1.1
-Note: email subscription sender job requires Django 1.1, everything else works with 1.0
-Make sure mysql for python provider has been installed.
-sudo easy_install mysql-python
-
-2. Python-openid v2.2
-http://openidenabled.com/python-openid/
-sudo easy_install python-openid
-
-4. html5lib
-http://code.google.com/p/html5lib/
-Used for HTML sanitizer
-sudo easy_install html5lib
-
-5. Markdown2
-http://code.google.com/p/python-markdown2/
-sudo easy_install markdown2
-
-6. Django Debug Toolbar
-http://github.com/robhudson/django-debug-toolbar/tree/master
-
-7. djangosphinx (optional - for full text questions+answer+tag)
-http://github.com/dcramer/django-sphinx/tree/master/djangosphinx
-
-8. sphinx search engine (optional, works together with djangosphinx)
-http://sphinxsearch.com/downloads.html
-
-9. recaptcha_django
-http://code.google.com/p/recaptcha-django/
-
-10. python recaptcha module
-http://code.google.com/p/recaptcha/
-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 OSQA code
-and is significantly modified. http://code.google.com/p/django-authopenid/
-no need to install this library
-
-B. INSTALLATION
------------------------------------------------
-0. Make sure you have all above python libraries installed.
-
- make 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
-
-Copy settings_local.py.dist to settings_local.py and
-update all your settings. Check settings.py and update
-it as well if necessory.
-Section C explains configuration paramaters.
-
-2. Database
-
-Prepare your database by using the same database/account
-configuration from above.
-e.g,
-create database 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 OSQA on the development server
-
-Run "python manage.py runserver" to startup django
-development environment.
-(Under Linux you can use command "python manage.py runserver `hostname -i`:8000",
-where you can use any other available number for the port)
-
-you might want to have DEBUG=True in the beginning of settings.py
-when using the test server
-
-4. Installation under Apache/WSGI
-
-4.1 Prepare wsgi script
-
-Make a file readable by your webserver with the following content:
-
----------
-import os
-import sys
-
-sys.path.insert(0,'/one/level/above') #insert to make sure that forum will be found
-sys.path.append('/one/level/above/OSQA') #maybe this is not necessary
-os.environ['DJANGO_SETTINGS_MODULE'] = 'OSQA.settings'
-import django.core.handlers.wsgi
-application = django.core.handlers.wsgi.WSGIHandler()
------------
-
-insert method is used for path because if the forum directory name
-is by accident the same as some other python module
-you wull see strange errors - forum won't be found even though
-it's in the python path. for example using name "test" is
-not a good idea - as there is a module with such name
-
-
-4.2 Configure webserver
-Settings below are not perfect but may be a good starting point
-
----------
-WSGISocketPrefix /path/to/socket/sock #must be readable and writable by apache
-WSGIPythonHome /usr/local #must be readable by apache
-WSGIPythonEggs /var/python/eggs #must be readable and writable by apache
-
-#NOTE: all urs below will need to be adjusted if
-#settings.FORUM_SCRIPT_ALIAS !='' (e.g. = 'forum/')
-#this allows "rooting" forum at http://example.com/forum, if you like
-
- ServerAdmin forum@example.com
- DocumentRoot /path/to/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 OSQA
- WSGIProcessGroup OSQA
-
- #force all content to be served as static files
- #otherwise django will be crunching images through itself wasting time
- Alias /m/ /path/to/osqa-site/forum/skins/
- Alias /upfiles/ /path/to/osqa-site/forum/upfiles/
-
- Order deny,allow
- Allow from all
-
-
- #this is your wsgi script described in the prev section
- WSGIScriptAlias / /path/to/osqa-site/osqa.wsgi
-
- #this will force admin interface to work only
- #through https (optional)
- #"nimda" is the secret spelling of "admin" ;)
-
- RewriteEngine on
- RewriteRule /nimda(.*)$ https://example.com/nimda$1 [L,R=301]
-
- CustomLog /var/log/httpd/OSQA/access_log common
- ErrorLog /var/log/httpd/OSQA/error_log
-
-#(optional) run admin interface under https
-
- ServerAdmin forum@example.com
- 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/osqa-site/osqa.wsgi
- CustomLog /var/log/httpd/OSQA/access_log common
- ErrorLog /var/log/httpd/OSQA/error_log
- DirectoryIndex index.html
-
--------------
-
-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 osqa index first time manually
-
- % indexer --config /path/to/sphinx.conf --index osqa
-
- setup cron job to rebuild index periodically with command
- your crontab entry may be something like
-
- 0 9,15,21 * * * /usr/local/bin/indexer --config /etc/sphinx/sphinx.conf --all --rotate >/dev/null 2>&1
- adjust it as necessary this one will reindex three times a day at 9am 3pm and 9pm
-
- if your forum grows very big ( good luck with that :) you'll
- need to two search indices one diff index and one main
- please refer to online sphinx search documentation for the information
- on the subject http://sphinxsearch.com/docs/
-
- in settings_local.py set
- USE_SPHINX_SEARCH=True
- adjust other settings that have SPHINX_* prefix accordingly
- remember that there must be trailing comma in parentheses for
- SHPINX_SEARCH_INDICES tuple - particlarly with just one item!
-
- in settings.py look for INSTALLED_APPS
- and uncomment #'djangosphinx',
-
-
-6. Email subscriptions
-
- This function at the moment requires Django 1.1
-
- edit paths in the file cron/send_email_alerts
- set up a cron job to call cron/send_email_alerts once or twice a day
- subscription sender may be tested manually in shell
- by calling cron/send_email_alerts
-
-7. Sitemap
-Sitemap will be available at /sitemap.xml
-e.g yoursite.com/forum/sitemap.xml
-
-google will be pinged each time question, answer or
-comment is saved or a question deleted
-
-for this to be useful - do register you sitemap with Google at
-https://www.google.com/webmasters/tools/
-
-8. Miscellaneous
-
-There are some demo scripts under sql_scripts folder,
-including badges and test accounts for CNProg.com. You
-don't need them to run your sample.
-
-C. CONFIGURATION PARAMETERS
-
-#the only parameter that needs to be touched in settings.py is
-DEBUG=False #set to True to enable debug mode
-
-#all forum parameters are set in file settings_local.py
-
-LOG_FILENAME = '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
-SERVER_EMAIL = ''
-DEFAULT_FROM_EMAIL = ''
-EMAIL_HOST_USER = ''
-EMAIL_HOST_PASSWORD = '' #not necessary if mailserver is run on local machine
-EMAIL_SUBJECT_PREFIX = '[OSQA] '
-EMAIL_HOST='osqa.com'
-EMAIL_PORT='25'
-EMAIL_USE_TLS=False
-TIME_ZONE = 'America/Tijuana'
-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'Ask and answer questions, make the world better!
' #slogan that goes to front page in logged out mode
-APP_COPYRIGHT = '' #copyright message
-
-#if you set FORUM_SCRIPT_ALIAS= 'forum/'
-#then 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
-
-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://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
-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 OSQA uses it's own login/password
-EXTERNAL_LEGACY_LOGIN_HOST = 'login.osqa.com'
-EXTERNAL_LEGACY_LOGIN_PORT = 80
-EXTERNAL_LEGACY_LOGIN_PROVIDER_NAME = 'OSQA'
-
-FEEDBACK_SITE_URL = None #None or url
-LOGIN_URL = '/%s%s%s' % (FORUM_SCRIPT_ALIAS,'account/','signin/')
-
-DJANGO_VERSION = 1.1 #must be either 1.0 or 1.1
-RESOURCE_REVISION=4 #increment when you update media files - clients will be forced to load new version
-
-D. Customization
-
-Other than settings_local.py the following will most likely need customization:
-* locale/*/django.po - language files that may also contain your site-specific messages
- if you want to start with english messages file - look for words like "forum" and
- "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
-* templates/content/style/style.css - modify style sheet to add disctinctive look to your forum
diff --git a/PENDING b/PENDING
deleted file mode 100644
index 2931303..0000000
--- a/PENDING
+++ /dev/null
@@ -1,28 +0,0 @@
-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==
-* validate HTML
-* set up loading of default settings from inside the /forum dir
-* automatic dependency checking for modules
-* propose how to rename directory forum --> osqa
- without breaking things and keeping name of the project root
- named the same way - osqa
-
-==New features==
-Whoever wants - pick a feature from the WISH_LIST
-add it here and start working on it
-If you are not starting immediately - leave it on the wishlist :)
-
-==Notes==
-1)after this is done most new suggested features
- may be worked on easily since most of them
- only require editing view functions and templates
-
- However, anyone can work on new features anyway - you'll
- just have to probably copy-paste your code into
- the branch undergoing refactoring which involves
- splitting the files. Auto merging across split points
- is harder or impossible.
diff --git a/README b/README
deleted file mode 100644
index 2a209b7..0000000
--- a/README
+++ /dev/null
@@ -1,6 +0,0 @@
-This is OSQA project - open source Q&A system
-
-Demo site is http://osqa.net
-
-OSQA is based on code of CNPROG, originally created by Mike Chen and Sailing Cai.
-
diff --git a/ROADMAP.rst b/ROADMAP.rst
deleted file mode 100644
index 42f2e8c..0000000
--- a/ROADMAP.rst
+++ /dev/null
@@ -1,32 +0,0 @@
-This document is a map for our activities down the road - therefore ROADMAP.
-ROADMAP does not specify deadlines - those belong to the PENDING file
-
-Intro
-=========
-ROADMAP aims to streamline activities of the OSQA open source project and
-to minimize ad-hoc approaches of "big-picture" level.
-
-With one exception: under extreme time pressure improvised approaches are perfectly acceptable.
-
-Items in this document must be discussed in public via dev@osqa.net
-
-Architecture
-=============
-
-Sub-systems
------------------
-* authentication system
-* Q&A system
-
-Authentication system
--------------------------
-* MUST authenticate people visiting the website via web browsers.
-* Upon successful authentication must associates the visitor with
- his/her Django system user account
-* MUST allow multiple methods of authentication to the same account
-* MUST support a method to recover lost authentication link by email
-* MAY offer an option to "soft-validate" user's email (send a link
- with a special key, so that user clicks and we know that email is valid)
- "soft" - meaning that lack of validation won't block people
- from using the site
-
diff --git a/WISH_LIST b/WISH_LIST
deleted file mode 100644
index 6b10687..0000000
--- a/WISH_LIST
+++ /dev/null
@@ -1,10 +0,0 @@
-* The wonder bar (integrated the search / ask functionality)
-* The authentication system ???
-* 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
diff --git a/__init__.py b/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/context.py b/context.py
deleted file mode 100644
index 9e22550..0000000
--- a/context.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from django.conf import settings
-def application_settings(context):
- my_settings = {
- 'APP_TITLE' : settings.APP_TITLE,
- 'APP_SHORT_NAME' : settings.APP_SHORT_NAME,
- 'APP_URL' : settings.APP_URL,
- 'APP_KEYWORDS' : settings.APP_KEYWORDS,
- 'APP_DESCRIPTION' : settings.APP_DESCRIPTION,
- 'APP_INTRO' : settings.APP_INTRO,
- 'EMAIL_VALIDATION': settings.EMAIL_VALIDATION,
- 'LANGUAGE_CODE': settings.LANGUAGE_CODE,
- 'GOOGLE_SITEMAP_CODE':settings.GOOGLE_SITEMAP_CODE,
- 'GOOGLE_ANALYTICS_KEY':settings.GOOGLE_ANALYTICS_KEY,
- 'BOOKS_ON':settings.BOOKS_ON,
- 'WIKI_ON':settings.WIKI_ON,
- '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}
-
-def auth_processor(request):
- """
- Returns context variables required by apps that use Django's authentication
- system.
-
- If there is no 'user' attribute in the request, uses AnonymousUser (from
- django.contrib.auth).
- """
- if hasattr(request, 'user'):
- user = request.user
- if user.is_authenticated():
- messages = user.message_set.all()
- else:
- messages = None
- else:
- from django.contrib.auth.models import AnonymousUser
- user = AnonymousUser()
- messages = None
-
- from django.core.context_processors import PermWrapper
- return {
- 'user': user,
- 'messages': messages,
- 'perms': PermWrapper(user),
- }
diff --git a/cron/send_email_alerts b/cron/send_email_alerts
deleted file mode 100644
index 6358b59..0000000
--- a/cron/send_email_alerts
+++ /dev/null
@@ -1,4 +0,0 @@
-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/dos2unix.sh b/dos2unix.sh
deleted file mode 100644
index 2864426..0000000
--- a/dos2unix.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#please take care not to dos2unix anything in your .git directory
-#because that will probably break your repo
-dos2unix `find . -name '*.py'`
-dos2unix `find . -name '*.po'`
-dos2unix `find . -name '*.js'`
-dos2unix `find . -name '*.css'`
-dos2unix `find . -name '*.txt'`
-dos2unix `find ./sphinx -type f`
-dos2unix `find ./cron -type f`
-dos2unix settings_local.py.dist
-dos2unix README
-dos2unix INSTALL
diff --git a/forum/__init__.py b/forum/__init__.py
deleted file mode 100644
index 85cd5d2..0000000
--- a/forum/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__all__ = ['admin','auth','const','feed','forms','managers','models','sitemap','urls','views']
diff --git a/forum/admin.py b/forum/admin.py
deleted file mode 100644
index 88643b9..0000000
--- a/forum/admin.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from django.contrib import admin
-from models import *
-
-
-class AnonymousQuestionAdmin(admin.ModelAdmin):
- """AnonymousQuestion admin class"""
-
-class QuestionAdmin(admin.ModelAdmin):
- """Question admin class"""
-
-class TagAdmin(admin.ModelAdmin):
- """Tag admin class"""
-
-class Answerdmin(admin.ModelAdmin):
- """Answer admin class"""
-
-class CommentAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class VoteAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class FlaggedItemAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class FavoriteQuestionAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class QuestionRevisionAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class AnswerRevisionAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class AwardAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class BadgeAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class ReputeAdmin(admin.ModelAdmin):
- """ admin class"""
-
-class ActivityAdmin(admin.ModelAdmin):
- """ admin class"""
-
-#class BookAdmin(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)
-admin.site.register(Answer, Answerdmin)
-admin.site.register(Comment, CommentAdmin)
-admin.site.register(Vote, VoteAdmin)
-admin.site.register(FlaggedItem, FlaggedItemAdmin)
-admin.site.register(FavoriteQuestion, FavoriteQuestionAdmin)
-admin.site.register(QuestionRevision, QuestionRevisionAdmin)
-admin.site.register(AnswerRevision, AnswerRevisionAdmin)
-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)
diff --git a/forum/auth.py b/forum/auth.py
deleted file mode 100644
index 3533b9c..0000000
--- a/forum/auth.py
+++ /dev/null
@@ -1,498 +0,0 @@
-"""
-Authorisation related functions.
-
-The actions a User is authorised to perform are dependent on their reputation
-and superuser status.
-"""
-import datetime
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext as _
-from django.db import transaction
-from models import Repute
-from models import Question
-from models import Answer
-from const import TYPE_REPUTATION
-import logging
-question_type = ContentType.objects.get_for_model(Question)
-answer_type = ContentType.objects.get_for_model(Answer)
-
-VOTE_UP = 15
-FLAG_OFFENSIVE = 15
-POST_IMAGES = 15
-LEAVE_COMMENTS = 50
-UPLOAD_FILES = 60
-VOTE_DOWN = 100
-CLOSE_OWN_QUESTIONS = 250
-RETAG_OTHER_QUESTIONS = 500
-REOPEN_OWN_QUESTIONS = 500
-EDIT_COMMUNITY_WIKI_POSTS = 750
-EDIT_OTHER_POSTS = 2000
-DELETE_COMMENTS = 2000
-VIEW_OFFENSIVE_FLAGS = 2000
-DISABLE_URL_NOFOLLOW = 2000
-CLOSE_OTHER_QUESTIONS = 3000
-LOCK_POSTS = 4000
-
-VOTE_RULES = {
- 'scope_votes_per_user_per_day' : 30, # how many votes of one user has everyday
- 'scope_flags_per_user_per_day' : 5, # how many times user can flag posts everyday
- 'scope_warn_votes_left' : 10, # start when to warn user how many votes left
- 'scope_deny_unvote_days' : 1, # if 1 days passed, user can't cancel votes.
- 'scope_flags_invisible_main_page' : 3, # post doesn't show on main page if has more than 3 offensive flags
- 'scope_flags_delete_post' : 5, # post will be deleted if it has more than 5 offensive flags
-}
-
-REPUTATION_RULES = {
- 'initial_score' : 1,
- 'scope_per_day_by_upvotes' : 200,
- 'gain_by_upvoted' : 10,
- 'gain_by_answer_accepted' : 15,
- 'gain_by_accepting_answer' : 2,
- 'gain_by_downvote_canceled' : 2,
- 'gain_by_canceling_downvote' : 1,
- 'lose_by_canceling_accepted_answer' : -2,
- 'lose_by_accepted_answer_cancled' : -15,
- 'lose_by_downvoted' : -2,
- 'lose_by_flagged' : -2,
- 'lose_by_downvoting' : -1,
- 'lose_by_flagged_lastrevision_3_times': -30,
- 'lose_by_flagged_lastrevision_5_times': -100,
- 'lose_by_upvote_canceled' : -10,
-}
-
-def can_moderate_users(user):
- return user.is_superuser
-
-def can_vote_up(user):
- """Determines if a User can vote Questions and Answers up."""
- return user.is_authenticated() and (
- user.reputation >= VOTE_UP or
- user.is_superuser)
-
-def can_flag_offensive(user):
- """Determines if a User can flag Questions and Answers as offensive."""
- return user.is_authenticated() and (
- user.reputation >= FLAG_OFFENSIVE or
- user.is_superuser)
-
-def can_add_comments(user,subject):
- """Determines if a User can add comments to Questions and Answers."""
- if user.is_authenticated():
- if user.id == subject.author.id:
- return True
- if user.reputation >= LEAVE_COMMENTS:
- return True
- if user.is_superuser:
- return True
- if isinstance(subject,Answer) and subject.question.author.id == user.id:
- return True
- return False
-
-def can_vote_down(user):
- """Determines if a User can vote Questions and Answers down."""
- return user.is_authenticated() and (
- user.reputation >= VOTE_DOWN or
- user.is_superuser)
-
-def can_retag_questions(user):
- """Determines if a User can retag Questions."""
- return user.is_authenticated() and (
- RETAG_OTHER_QUESTIONS <= user.reputation < EDIT_OTHER_POSTS or
- user.is_superuser)
-
-def can_edit_post(user, post):
- """Determines if a User can edit the given Question or Answer."""
- return user.is_authenticated() and (
- user.id == post.author_id or
- (post.wiki and user.reputation >= EDIT_COMMUNITY_WIKI_POSTS) or
- user.reputation >= EDIT_OTHER_POSTS or
- user.is_superuser)
-
-def can_delete_comment(user, comment):
- """Determines if a User can delete the given Comment."""
- return user.is_authenticated() and (
- user.id == comment.user_id or
- user.reputation >= DELETE_COMMENTS or
- user.is_superuser)
-
-def can_view_offensive_flags(user):
- """Determines if a User can view offensive flag counts."""
- return user.is_authenticated() and (
- user.reputation >= VIEW_OFFENSIVE_FLAGS or
- user.is_superuser)
-
-def can_close_question(user, question):
- """Determines if a User can close the given Question."""
- return user.is_authenticated() and (
- (user.id == question.author_id and
- user.reputation >= CLOSE_OWN_QUESTIONS) or
- user.reputation >= CLOSE_OTHER_QUESTIONS or
- user.is_superuser)
-
-def can_lock_posts(user):
- """Determines if a User can lock Questions or Answers."""
- return user.is_authenticated() and (
- user.reputation >= LOCK_POSTS or
- user.is_superuser)
-
-def can_follow_url(user):
- """Determines if the URL link can be followed by Google search engine."""
- return user.reputation >= DISABLE_URL_NOFOLLOW
-
-def can_accept_answer(user, question, answer):
- return (user.is_authenticated() and
- question.author != answer.author and
- question.author == user) or user.is_superuser
-
-# now only support to reopen own question except superuser
-def can_reopen_question(user, question):
- return (user.is_authenticated() and
- user.id == question.author_id and
- user.reputation >= REOPEN_OWN_QUESTIONS) or user.is_superuser
-
-def can_delete_post(user, post):
- if user.is_superuser:
- return True
- elif user.is_authenticated() and user == post.author:
- if isinstance(post,Answer):
- return True
- elif isinstance(post,Question):
- answers = post.answers.all()
- for answer in answers:
- if user != answer.author and answer.deleted == False:
- return False
- return True
- else:
- return False
- else:
- return False
-
-def can_view_deleted_post(user, post):
- return user.is_superuser
-
-# user preferences view permissions
-def is_user_self(request_user, target_user):
- return (request_user.is_authenticated() and request_user == target_user)
-
-def can_view_user_votes(request_user, target_user):
- return (request_user.is_authenticated() and request_user == target_user)
-
-def can_view_user_preferences(request_user, target_user):
- return (request_user.is_authenticated() and request_user == target_user)
-
-def can_view_user_edit(request_user, target_user):
- return (request_user.is_authenticated() and request_user == target_user)
-
-def can_upload_files(request_user):
- return (request_user.is_authenticated() and request_user.reputation >= UPLOAD_FILES) or \
- request_user.is_superuser
-
-###########################################
-## actions and reputation changes event
-###########################################
-def calculate_reputation(origin, offset):
- result = int(origin) + int(offset)
- if (result > 0):
- return result
- else:
- return 1
-
-@transaction.commit_on_success
-def onFlaggedItem(item, post, user):
-
- item.save()
- post.offensive_flag_count = post.offensive_flag_count + 1
- post.save()
-
- post.author.reputation = calculate_reputation(post.author.reputation,
- int(REPUTATION_RULES['lose_by_flagged']))
- post.author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=post.author,
- negative=int(REPUTATION_RULES['lose_by_flagged']),
- question=question, reputed_at=datetime.datetime.now(),
- reputation_type=-4,
- reputation=post.author.reputation)
- reputation.save()
-
- #todo: These should be updated to work on same revisions.
- if post.offensive_flag_count == VOTE_RULES['scope_flags_invisible_main_page'] :
- post.author.reputation = calculate_reputation(post.author.reputation,
- int(REPUTATION_RULES['lose_by_flagged_lastrevision_3_times']))
- post.author.save()
-
- reputation = Repute(user=post.author,
- negative=int(REPUTATION_RULES['lose_by_flagged_lastrevision_3_times']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-6,
- reputation=post.author.reputation)
- reputation.save()
-
- elif post.offensive_flag_count == VOTE_RULES['scope_flags_delete_post']:
- post.author.reputation = calculate_reputation(post.author.reputation,
- int(REPUTATION_RULES['lose_by_flagged_lastrevision_5_times']))
- post.author.save()
-
- reputation = Repute(user=post.author,
- negative=int(REPUTATION_RULES['lose_by_flagged_lastrevision_5_times']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-7,
- reputation=post.author.reputation)
- reputation.save()
-
- post.deleted = True
- #post.deleted_at = datetime.datetime.now()
- #post.deleted_by = Admin
- post.save()
-
-
-@transaction.commit_on_success
-def onAnswerAccept(answer, user):
- answer.accepted = True
- answer.accepted_at = datetime.datetime.now()
- answer.question.answer_accepted = True
- answer.save()
- answer.question.save()
-
- answer.author.reputation = calculate_reputation(answer.author.reputation,
- int(REPUTATION_RULES['gain_by_answer_accepted']))
- answer.author.save()
- reputation = Repute(user=answer.author,
- positive=int(REPUTATION_RULES['gain_by_answer_accepted']),
- question=answer.question,
- reputed_at=datetime.datetime.now(),
- reputation_type=2,
- reputation=answer.author.reputation)
- reputation.save()
-
- user.reputation = calculate_reputation(user.reputation,
- int(REPUTATION_RULES['gain_by_accepting_answer']))
- user.save()
- reputation = Repute(user=user,
- positive=int(REPUTATION_RULES['gain_by_accepting_answer']),
- question=answer.question,
- reputed_at=datetime.datetime.now(),
- reputation_type=3,
- reputation=user.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onAnswerAcceptCanceled(answer, user):
- answer.accepted = False
- answer.accepted_at = None
- answer.question.answer_accepted = False
- answer.save()
- answer.question.save()
-
- answer.author.reputation = calculate_reputation(answer.author.reputation,
- int(REPUTATION_RULES['lose_by_accepted_answer_cancled']))
- answer.author.save()
- reputation = Repute(user=answer.author,
- negative=int(REPUTATION_RULES['lose_by_accepted_answer_cancled']),
- question=answer.question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-2,
- reputation=answer.author.reputation)
- reputation.save()
-
- user.reputation = calculate_reputation(user.reputation,
- int(REPUTATION_RULES['lose_by_canceling_accepted_answer']))
- user.save()
- reputation = Repute(user=user,
- negative=int(REPUTATION_RULES['lose_by_canceling_accepted_answer']),
- question=answer.question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-1,
- reputation=user.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onUpVoted(vote, post, user):
- vote.save()
-
- post.vote_up_count = int(post.vote_up_count) + 1
- post.score = int(post.score) + 1
- post.save()
-
- if not post.wiki:
- author = post.author
- if Repute.objects.get_reputation_by_upvoted_today(author) < int(REPUTATION_RULES['scope_per_day_by_upvotes']):
- author.reputation = calculate_reputation(author.reputation,
- int(REPUTATION_RULES['gain_by_upvoted']))
- author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=author,
- positive=int(REPUTATION_RULES['gain_by_upvoted']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=1,
- reputation=author.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onUpVotedCanceled(vote, post, user):
- vote.delete()
-
- post.vote_up_count = int(post.vote_up_count) - 1
- if post.vote_up_count < 0:
- post.vote_up_count = 0
- post.score = int(post.score) - 1
- post.save()
-
- if not post.wiki:
- author = post.author
- author.reputation = calculate_reputation(author.reputation,
- int(REPUTATION_RULES['lose_by_upvote_canceled']))
- author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=author,
- negative=int(REPUTATION_RULES['lose_by_upvote_canceled']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-8,
- reputation=author.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onDownVoted(vote, post, user):
- vote.save()
-
- post.vote_down_count = int(post.vote_down_count) + 1
- post.score = int(post.score) - 1
- post.save()
-
- if not post.wiki:
- author = post.author
- author.reputation = calculate_reputation(author.reputation,
- int(REPUTATION_RULES['lose_by_downvoted']))
- author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=author,
- negative=int(REPUTATION_RULES['lose_by_downvoted']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-3,
- reputation=author.reputation)
- reputation.save()
-
- user.reputation = calculate_reputation(user.reputation,
- int(REPUTATION_RULES['lose_by_downvoting']))
- user.save()
-
- reputation = Repute(user=user,
- negative=int(REPUTATION_RULES['lose_by_downvoting']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=-5,
- reputation=user.reputation)
- reputation.save()
-
-@transaction.commit_on_success
-def onDownVotedCanceled(vote, post, user):
- vote.delete()
-
- post.vote_down_count = int(post.vote_down_count) - 1
- if post.vote_down_count < 0:
- post.vote_down_count = 0
- post.score = post.score + 1
- post.save()
-
- if not post.wiki:
- author = post.author
- author.reputation = calculate_reputation(author.reputation,
- int(REPUTATION_RULES['gain_by_downvote_canceled']))
- author.save()
-
- question = post
- if ContentType.objects.get_for_model(post) == answer_type:
- question = post.question
-
- reputation = Repute(user=author,
- positive=int(REPUTATION_RULES['gain_by_downvote_canceled']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=4,
- reputation=author.reputation)
- reputation.save()
-
- user.reputation = calculate_reputation(user.reputation,
- int(REPUTATION_RULES['gain_by_canceling_downvote']))
- user.save()
-
- reputation = Repute(user=user,
- positive=int(REPUTATION_RULES['gain_by_canceling_downvote']),
- question=question,
- reputed_at=datetime.datetime.now(),
- reputation_type=5,
- reputation=user.reputation)
- reputation.save()
-
-def onDeleteCanceled(post, user):
- post.deleted = False
- post.deleted_by = None
- post.deleted_at = None
- post.save()
- logging.debug('now restoring something')
- if isinstance(post,Answer):
- logging.debug('updated answer count on undelete, have %d' % post.question.answer_count)
- Question.objects.update_answer_count(post.question)
- elif isinstance(post,Question):
- for tag in list(post.tags.all()):
- if tag.used_count == 1 and tag.deleted:
- tag.deleted = False
- tag.deleted_by = None
- tag.deleted_at = None
- tag.save()
-
-def onDeleted(post, user):
- post.deleted = True
- post.deleted_by = user
- post.deleted_at = datetime.datetime.now()
- post.save()
-
- if isinstance(post, Question):
- for tag in list(post.tags.all()):
- if tag.used_count == 1:
- tag.deleted = True
- tag.deleted_by = user
- tag.deleted_at = datetime.datetime.now()
- else:
- tag.used_count = tag.used_count - 1
- tag.save()
-
- answers = post.answers.all()
- if user == post.author:
- if len(answers) > 0:
- msg = _('Your question and all of it\'s answers have been deleted')
- else:
- msg = _('Your question has been deleted')
- else:
- if len(answers) > 0:
- msg = _('The question and all of it\'s answers have been deleted')
- else:
- msg = _('The question has been deleted')
- user.message_set.create(message=msg)
- logging.debug('posted a message %s' % msg)
- for answer in answers:
- onDeleted(answer, user)
- elif isinstance(post, Answer):
- Question.objects.update_answer_count(post.question)
- logging.debug('updated answer count to %d' % post.question.answer_count)
diff --git a/forum/authentication/__init__.py b/forum/authentication/__init__.py
deleted file mode 100755
index e83ba87..0000000
--- a/forum/authentication/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import re
-from forum.modules import get_modules_script_classes
-from forum.authentication.base import AuthenticationConsumer, ConsumerTemplateContext
-
-class ConsumerAndContext():
- def __init__(self, id, consumer, context):
- self.id = id
- self.consumer = consumer()
-
- context.id = id
- self.context = context
-
-consumers = dict([
- (re.sub('AuthConsumer$', '', name).lower(), cls) for name, cls
- in get_modules_script_classes('authentication', AuthenticationConsumer).items()
- if not re.search('AbstractAuthConsumer$', name)
- ])
-
-contexts = dict([
- (re.sub('AuthContext$', '', name).lower(), cls) for name, cls
- in get_modules_script_classes('authentication', ConsumerTemplateContext).items()
- ])
-
-AUTH_PROVIDERS = dict([
- (name, ConsumerAndContext(name, consumers[name], contexts[name])) for name in consumers.keys()
- if name in contexts
- ])
\ No newline at end of file
diff --git a/forum/authentication/base.py b/forum/authentication/base.py
deleted file mode 100755
index 995f7c9..0000000
--- a/forum/authentication/base.py
+++ /dev/null
@@ -1,40 +0,0 @@
-
-class AuthenticationConsumer(object):
-
- def prepare_authentication_request(self, request, redirect_to):
- raise NotImplementedError()
-
- def process_authentication_request(self, response):
- raise NotImplementedError()
-
- def get_user_data(self, key):
- raise NotImplementedError()
-
-
-class ConsumerTemplateContext(object):
- """
- Class that provides information about a certain authentication provider context in the signin page.
-
- class attributes:
-
- mode - one of BIGICON, SMALLICON, FORM
-
- human_name - the human readable name of the provider
-
- extra_js - some providers require us to load extra javascript on the signin page for them to work,
- this is the place to add those files in the form of a list
-
- extra_css - same as extra_js but for css files
- """
- mode = ''
- weight = 500
- human_name = ''
- extra_js = []
- extra_css = []
- show_to_logged_in_user = True
-
-class InvalidAuthentication(Exception):
- def __init__(self, message):
- self.message = message
-
-
\ No newline at end of file
diff --git a/forum/authentication/forms.py b/forum/authentication/forms.py
deleted file mode 100755
index 0484134..0000000
--- a/forum/authentication/forms.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from forum.utils.forms import NextUrlField, UserNameField, UserEmailField
-from forum.models import EmailFeedSetting, Question
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext as _
-from django import forms
-from forum.forms import EditUserEmailFeedsForm
-import logging
-
-class SimpleRegistrationForm(forms.Form):
- next = NextUrlField()
- username = UserNameField()
- email = UserEmailField()
-
-
-class SimpleEmailSubscribeForm(forms.Form):
- SIMPLE_SUBSCRIBE_CHOICES = (
- ('y',_('okay, let\'s try!')),
- ('n',_('no OSQA community email please, thanks'))
- )
- subscribe = forms.ChoiceField(widget=forms.widgets.RadioSelect(), \
- error_messages={'required':_('please choose one of the options above')},
- choices=SIMPLE_SUBSCRIBE_CHOICES)
-
- def save(self,user=None):
- EFF = EditUserEmailFeedsForm
- if self.cleaned_data['subscribe'] == 'y':
- email_settings_form = EFF()
- logging.debug('%s wants to subscribe' % user.username)
- else:
- email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL)
- email_settings_form.save(user,save_unbound=True)
diff --git a/forum/const.py b/forum/const.py
deleted file mode 100644
index 76fd4a2..0000000
--- a/forum/const.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# encoding:utf-8
-from django.utils.translation import ugettext as _
-"""
-All constants could be used in other modules
-For reasons that models, views can't have unicode text in this project, all unicode text go here.
-"""
-CLOSE_REASONS = (
- (1, _('duplicate question')),
- (2, _('question is off-topic or not relevant')),
- (3, _('too subjective and argumentative')),
- (4, _('is not an answer to the question')),
- (5, _('the question is answered, right answer was accepted')),
- (6, _('problem is not reproducible or outdated')),
- #(7, u'太å±é¨ãæ¬å°åçé®é¢',)
- (7, _('question contains offensive inappropriate, or malicious remarks')),
- (8, _('spam or advertising')),
-)
-
-TYPE_REPUTATION = (
- (1, 'gain_by_upvoted'),
- (2, 'gain_by_answer_accepted'),
- (3, 'gain_by_accepting_answer'),
- (4, 'gain_by_downvote_canceled'),
- (5, 'gain_by_canceling_downvote'),
- (-1, 'lose_by_canceling_accepted_answer'),
- (-2, 'lose_by_accepted_answer_cancled'),
- (-3, 'lose_by_downvoted'),
- (-4, 'lose_by_flagged'),
- (-5, 'lose_by_downvoting'),
- (-6, 'lose_by_flagged_lastrevision_3_times'),
- (-7, 'lose_by_flagged_lastrevision_5_times'),
- (-8, 'lose_by_upvote_canceled'),
-)
-
-TYPE_ACTIVITY_ASK_QUESTION=1
-TYPE_ACTIVITY_ANSWER=2
-TYPE_ACTIVITY_COMMENT_QUESTION=3
-TYPE_ACTIVITY_COMMENT_ANSWER=4
-TYPE_ACTIVITY_UPDATE_QUESTION=5
-TYPE_ACTIVITY_UPDATE_ANSWER=6
-TYPE_ACTIVITY_PRIZE=7
-TYPE_ACTIVITY_MARK_ANSWER=8
-TYPE_ACTIVITY_VOTE_UP=9
-TYPE_ACTIVITY_VOTE_DOWN=10
-TYPE_ACTIVITY_CANCEL_VOTE=11
-TYPE_ACTIVITY_DELETE_QUESTION=12
-TYPE_ACTIVITY_DELETE_ANSWER=13
-TYPE_ACTIVITY_MARK_OFFENSIVE=14
-TYPE_ACTIVITY_UPDATE_TAGS=15
-TYPE_ACTIVITY_FAVORITE=16
-TYPE_ACTIVITY_USER_FULL_UPDATED = 17
-TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT = 18
-#TYPE_ACTIVITY_EDIT_QUESTION=17
-#TYPE_ACTIVITY_EDIT_ANSWER=18
-
-TYPE_ACTIVITY = (
- (TYPE_ACTIVITY_ASK_QUESTION, _('question')),
- (TYPE_ACTIVITY_ANSWER, _('answer')),
- (TYPE_ACTIVITY_COMMENT_QUESTION, _('commented question')),
- (TYPE_ACTIVITY_COMMENT_ANSWER, _('commented answer')),
- (TYPE_ACTIVITY_UPDATE_QUESTION, _('edited question')),
- (TYPE_ACTIVITY_UPDATE_ANSWER, _('edited answer')),
- (TYPE_ACTIVITY_PRIZE, _('received award')),
- (TYPE_ACTIVITY_MARK_ANSWER, _('marked best answer')),
- (TYPE_ACTIVITY_VOTE_UP, _('upvoted')),
- (TYPE_ACTIVITY_VOTE_DOWN, _('downvoted')),
- (TYPE_ACTIVITY_CANCEL_VOTE, _('canceled vote')),
- (TYPE_ACTIVITY_DELETE_QUESTION, _('deleted question')),
- (TYPE_ACTIVITY_DELETE_ANSWER, _('deleted answer')),
- (TYPE_ACTIVITY_MARK_OFFENSIVE, _('marked offensive')),
- (TYPE_ACTIVITY_UPDATE_TAGS, _('updated tags')),
- (TYPE_ACTIVITY_FAVORITE, _('selected favorite')),
- (TYPE_ACTIVITY_USER_FULL_UPDATED, _('completed user profile')),
- (TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT, _('email update sent to user')),
-)
-
-TYPE_RESPONSE = {
- 'QUESTION_ANSWERED' : 'question_answered',
- 'QUESTION_COMMENTED': 'question_commented',
- 'ANSWER_COMMENTED' : 'answer_commented',
- 'ANSWER_ACCEPTED' : 'answer_accepted',
-}
-
-CONST = {
- 'closed' : _('[closed]'),
- 'deleted' : _('[deleted]'),
- 'default_version' : _('initial version'),
- 'retagged' : _('retagged'),
-}
-
-#how to filter questions by tags in email digests?
-TAG_EMAIL_FILTER_CHOICES = (('ignored', _('exclude ignored tags')),('interesting',_('allow only selected tags')))
diff --git a/forum/feed.py b/forum/feed.py
deleted file mode 100644
index e4b929e..0000000
--- a/forum/feed.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python
-#encoding:utf-8
-#-------------------------------------------------------------------------------
-# Name: Syndication feed class for subsribtion
-# Purpose:
-#
-# Author: Mike
-#
-# Created: 29/01/2009
-# Copyright: (c) CNPROG.COM 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
-from django.utils.translation import ugettext as _
-from models import Question
-from django.conf import settings
-class RssLastestQuestionsFeed(Feed):
- title = settings.APP_TITLE + _(' - ')+ _('latest questions')
- link = settings.APP_URL #+ '/' + _('question/')
- description = settings.APP_DESCRIPTION
- #ttl = 10
- copyright = settings.APP_COPYRIGHT
-
- def item_link(self, item):
- return self.link + item.get_absolute_url()
-
- def item_author_name(self, item):
- return item.author.username
-
- def item_author_link(self, item):
- return item.author.get_profile_url()
-
- def item_pubdate(self, item):
- return item.added_at
-
- def items(self, item):
- return Question.objects.filter(deleted=False).order_by('-last_activity_at')[:30]
-
-def main():
- pass
-
-if __name__ == '__main__':
- main()
diff --git a/forum/forms.py b/forum/forms.py
deleted file mode 100644
index c157aa4..0000000
--- a/forum/forms.py
+++ /dev/null
@@ -1,359 +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 django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
-from django.utils.safestring import mark_safe
-from forum.utils.forms import NextUrlField, UserNameField, SetPasswordForm
-from recaptcha_django import ReCaptchaField
-from django.conf import settings
-import logging
-
-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}))
- if settings.EDITABLE_SCREEN_NAME:
- 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)
- logging.debug('initializing the form')
- if settings.EDITABLE_SCREEN_NAME:
- 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 ChangePasswordForm(SetPasswordForm):
- """ change password form """
- oldpw = forms.CharField(widget=forms.PasswordInput(attrs={'class':'required'}),
- label=mark_safe(_('Current password')))
-
- def __init__(self, data=None, user=None, *args, **kwargs):
- if user is None:
- raise TypeError("Keyword argument 'user' must be supplied")
- super(ChangePasswordForm, self).__init__(data, *args, **kwargs)
- self.user = user
-
- def clean_oldpw(self):
- """ test old password """
- if not self.user.check_password(self.cleaned_data['oldpw']):
- raise forms.ValidationError(_("Old password is incorrect. \
- Please enter the correct password."))
- return self.cleaned_data['oldpw']
-
-class EditUserEmailFeedsForm(forms.Form):
- WN = (('w',_('weekly')),('n',_('no email')))
- DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
- 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
-
diff --git a/forum/management/__init__.py b/forum/management/__init__.py
deleted file mode 100644
index 8266592..0000000
--- a/forum/management/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from forum.modules import get_modules_script
-
-get_modules_script('management')
\ No newline at end of file
diff --git a/forum/management/commands/__init__.py b/forum/management/commands/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/forum/management/commands/base_command.py b/forum/management/commands/base_command.py
deleted file mode 100644
index c073bf7..0000000
--- a/forum/management/commands/base_command.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env python
-#encoding:utf-8
-#-------------------------------------------------------------------------------
-# Name: Award badges command
-# Purpose: This is a command file croning in background process regularly to
-# query database and award badges for user's special acitivities.
-#
-# Author: Mike, Sailing
-#
-# Created: 22/01/2009
-# Copyright: (c) Mike 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-
-from datetime import datetime, date
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.shortcuts import get_object_or_404
-from django.contrib.contenttypes.models import ContentType
-
-from forum.models import *
-from forum.const import *
-
-class BaseCommand(NoArgsCommand):
- def update_activities_auditted(self, cursor, activity_ids):
- # update processed rows to auditted
- if len(activity_ids):
- query = "UPDATE activity SET is_auditted = 1 WHERE id in (%s)"\
- % ','.join('%s' % item for item in activity_ids)
- cursor.execute(query)
-
-
-
-
-
diff --git a/forum/management/commands/clean_award_badges.py b/forum/management/commands/clean_award_badges.py
deleted file mode 100644
index 117e3a5..0000000
--- a/forum/management/commands/clean_award_badges.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#-------------------------------------------------------------------------------
-# Name: Award badges command
-# Purpose: This is a command file croning in background process regularly to
-# query database and award badges for user's special acitivities.
-#
-# Author: Mike
-#
-# Created: 18/01/2009
-# Copyright: (c) Mike 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-#!/usr/bin/env python
-#encoding:utf-8
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.shortcuts import get_object_or_404
-from django.contrib.contenttypes.models import ContentType
-
-from forum.models import *
-
-class Command(NoArgsCommand):
- def handle_noargs(self, **options):
- try:
- try:
- self.clean_awards()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def clean_awards(self):
- Award.objects.all().delete()
-
- award_type =ContentType.objects.get_for_model(Award)
- Activity.objects.filter(content_type=award_type).delete()
-
- for user in User.objects.all():
- user.gold = 0
- user.silver = 0
- user.bronze = 0
- user.save()
-
- for badge in Badge.objects.all():
- badge.awarded_count = 0
- badge.save()
-
- query = "UPDATE activity SET is_auditted = 0"
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- finally:
- cursor.close()
- connection.close()
-
-def main():
- pass
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/forum/management/commands/message_to_everyone.py b/forum/management/commands/message_to_everyone.py
deleted file mode 100644
index c020c17..0000000
--- a/forum/management/commands/message_to_everyone.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.core.management.base import NoArgsCommand
-from django.contrib.auth.models import User
-import sys
-
-class Command(NoArgsCommand):
- def handle_noargs(self, **options):
- msg = None
- if msg == None:
- print 'to run this command, please first edit the file %s' % __file__
- sys.exit(1)
- for u in User.objects.all():
- u.message_set.create(message = msg % u.username)
diff --git a/forum/management/commands/multi_award_badges.py b/forum/management/commands/multi_award_badges.py
deleted file mode 100644
index 6b330cf..0000000
--- a/forum/management/commands/multi_award_badges.py
+++ /dev/null
@@ -1,348 +0,0 @@
-#!/usr/bin/env python
-#encoding:utf-8
-#-------------------------------------------------------------------------------
-# Name: Award badges command
-# Purpose: This is a command file croning in background process regularly to
-# query database and award badges for user's special acitivities.
-#
-# Author: Mike, Sailing
-#
-# Created: 22/01/2009
-# Copyright: (c) Mike 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-
-from datetime import datetime, date
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.shortcuts import get_object_or_404
-from django.contrib.contenttypes.models import ContentType
-
-from forum.models import *
-from forum.const import *
-from base_command import BaseCommand
-"""
-(1, 'ç¼ç±æ³å¸', 3, 'ç¼ç±æ³å¸', 'å é¤èªå·±æ3个以ä¸èµæ票çå¸å', 1, 0),
-(2, 'ååç½é¢', 3, 'ååç½é¢', 'å é¤èªå·±æ3个以ä¸å对票çå¸å', 1, 0),
-(3, 'ä¼ç§åç', 3, 'ä¼ç§åç', 'åç好è¯10次以ä¸', 1, 0),
-(4, 'ä¼ç§é®é¢', 3, 'ä¼ç§é®é¢', 'é®é¢å¥½è¯10次以ä¸', 1, 0),
-(5, 'è¯è®ºå®¶', 3, 'è¯è®ºå®¶', 'è¯è®º10次以ä¸', 0, 0),
-(6, 'æµè¡é®é¢', 3, 'æµè¡é®é¢', 'é®é¢çæµè§éè¶
è¿1000人次', 1, 0),
-(7, 'å·¡é»å
µ', 3, 'å·¡é»å
µ', '第ä¸æ¬¡æ è®°åå¾å¸å', 0, 0),
-(8, 'æ¸
æ´å·¥', 3, 'æ¸
æ´å·¥', '第ä¸æ¬¡æ¤éæ票', 0, 0),
-(9, 'æ¹è¯å®¶', 3, 'æ¹è¯å®¶', '第ä¸æ¬¡å对票', 0, 0),
-(10, 'å°ç¼', 3, 'å°ç¼', '第ä¸æ¬¡ç¼è¾æ´æ°', 0, 0),
-(11, 'æé¿', 3, 'æé¿', '第ä¸æ¬¡éæ°æ ç¾', 0, 0),
-(12, 'å¦è
', 3, 'å¦è
', '第ä¸æ¬¡æ è®°çæ¡', 0, 0),
-(13, 'å¦ç', 3, 'å¦ç', '第ä¸æ¬¡æé®å¹¶ä¸æä¸æ¬¡ä»¥ä¸èµæ票', 0, 0),
-(14, 'æ¯æè
', 3, 'æ¯æè
', '第ä¸æ¬¡èµæ票', 0, 0),
-(15, 'æå¸', 3, 'æå¸', '第ä¸æ¬¡åçé®é¢å¹¶ä¸å¾å°ä¸ä¸ªä»¥ä¸èµæ票', 0, 0),
-(16, 'èªä¼ ä½è
', 3, 'èªä¼ ä½è
', 'å®æ´å¡«åç¨æ·èµæææé项', 0, 0),
-(17, 'èªå¦ææ', 3, 'èªå¦ææ', 'åçèªå·±çé®é¢å¹¶ä¸æ3个以ä¸èµæ票', 1, 0),
-(18, 'ææä»·å¼åç', 1, 'ææä»·å¼åç', 'åçè¶
è¿100次èµæ票', 1, 0),
-(19, 'ææä»·å¼é®é¢', 1, 'ææä»·å¼é®é¢', 'é®é¢è¶
è¿100次èµæ票', 1, 0),
-(20, 'ä¸äººè¿·', 1, 'ä¸äººè¿·', 'é®é¢è¢«100人以ä¸æ¶è', 1, 0),
-(21, 'èåé®é¢', 1, 'èåé®é¢', 'é®é¢çæµè§éè¶
è¿10000人次', 1, 0),
-(22, 'alphaç¨æ·', 2, 'alphaç¨æ·', 'å
æµæé´çæ´»è·ç¨æ·', 0, 0),
-(23, 'æ好åç', 2, 'æ好åç', 'åçè¶
è¿25次èµæ票', 1, 0),
-(24, 'æ好é®é¢', 2, 'æ好é®é¢', 'é®é¢è¶
è¿25次èµæ票', 1, 0),
-(25, 'å欢è¿é®é¢', 2, 'å欢è¿é®é¢', 'é®é¢è¢«25人以ä¸æ¶è', 1, 0),
-(26, 'ä¼ç§å¸æ°', 2, 'ä¼ç§å¸æ°', 'æ票300次以ä¸', 0, 0),
-(27, 'ç¼è¾ä¸»ä»»', 2, 'ç¼è¾ä¸»ä»»', 'ç¼è¾äº100个å¸å', 0, 0),
-(28, 'éæ', 2, 'éæ', 'å¨å¤ä¸ªæ ç¾é¢åæ´»è·', 0, 0),
-(29, 'ä¸å®¶', 2, 'ä¸å®¶', 'å¨ä¸ä¸ªæ ç¾é¢åæ´»è·åºä¼', 0, 0),
-(30, 'èé¸', 2, 'èé¸', 'æ´»è·è¶
è¿ä¸å¹´çç¨æ·', 0, 0),
-(31, 'æåå
³æ³¨é®é¢', 2, 'æåå
³æ³¨é®é¢', 'é®é¢çæµè§éè¶
è¿2500人次', 1, 0),
-(32, 'å¦é®å®¶', 2, 'å¦é®å®¶', '第ä¸æ¬¡åç被æèµæ票10次以ä¸', 0, 0),
-(33, 'betaç¨æ·', 2, 'betaç¨æ·', 'betaæé´æ´»è·åä¸', 0, 0),
-(34, '导å¸', 2, '导å¸', '被æå®ä¸ºæä½³çæ¡å¹¶ä¸èµæ票40以ä¸', 1, 0),
-(35, 'å·«å¸', 2, 'å·«å¸', 'å¨æé®60天ä¹ååç并ä¸èµæ票5次以ä¸', 1, 0),
-(36, 'åç±»ä¸å®¶', 2, 'åç±»ä¸å®¶', 'å建çæ ç¾è¢«50个以ä¸é®é¢ä½¿ç¨', 1, 0);
-
-
-TYPE_ACTIVITY_ASK_QUESTION=1
-TYPE_ACTIVITY_ANSWER=2
-TYPE_ACTIVITY_COMMENT_QUESTION=3
-TYPE_ACTIVITY_COMMENT_ANSWER=4
-TYPE_ACTIVITY_UPDATE_QUESTION=5
-TYPE_ACTIVITY_UPDATE_ANSWER=6
-TYPE_ACTIVITY_PRIZE=7
-TYPE_ACTIVITY_MARK_ANSWER=8
-TYPE_ACTIVITY_VOTE_UP=9
-TYPE_ACTIVITY_VOTE_DOWN=10
-TYPE_ACTIVITY_CANCEL_VOTE=11
-TYPE_ACTIVITY_DELETE_QUESTION=12
-TYPE_ACTIVITY_DELETE_ANSWER=13
-TYPE_ACTIVITY_MARK_OFFENSIVE=14
-TYPE_ACTIVITY_UPDATE_TAGS=15
-TYPE_ACTIVITY_FAVORITE=16
-TYPE_ACTIVITY_USER_FULL_UPDATED = 17
-"""
-
-class Command(BaseCommand):
- def handle_noargs(self, **options):
- try:
- try:
- self.delete_question_be_voted_up_3()
- self.delete_answer_be_voted_up_3()
- self.delete_question_be_vote_down_3()
- self.delete_answer_be_voted_down_3()
- self.answer_be_voted_up_10()
- self.question_be_voted_up_10()
- self.question_view_1000()
- self.answer_self_question_be_voted_up_3()
- self.answer_be_voted_up_100()
- self.question_be_voted_up_100()
- self.question_be_favorited_100()
- self.question_view_10000()
- self.answer_be_voted_up_25()
- self.question_be_voted_up_25()
- self.question_be_favorited_25()
- self.question_view_2500()
- self.answer_be_accepted_and_voted_up_40()
- self.question_be_answered_after_60_days_and_be_voted_up_5()
- self.created_tag_be_used_in_question_50()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def delete_question_be_voted_up_3(self):
- """
- (1, 'ç¼ç±æ³å¸', 3, 'ç¼ç±æ³å¸', 'å é¤èªå·±æ3个以ä¸èµæ票çå¸å', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM activity act, question q WHERE act.object_id = q.id AND\
- act.activity_type = %s AND\
- q.vote_up_count >=3 AND \
- act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_QUESTION)
- self.__process_activities_badge(query, 1, Question)
-
- def delete_answer_be_voted_up_3(self):
- """
- (1, 'ç¼ç±æ³å¸', 3, 'ç¼ç±æ³å¸', 'å é¤èªå·±æ3个以ä¸èµæ票çå¸å', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM activity act, answer an WHERE act.object_id = an.id AND\
- act.activity_type = %s AND\
- an.vote_up_count >=3 AND \
- act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_ANSWER)
- self.__process_activities_badge(query, 1, Answer)
-
- def delete_question_be_vote_down_3(self):
- """
- (2, 'ååç½é¢', 3, 'ååç½é¢', 'å é¤èªå·±æ3个以ä¸å对票çå¸å', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM activity act, question q WHERE act.object_id = q.id AND\
- act.activity_type = %s AND\
- q.vote_down_count >=3 AND \
- act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_QUESTION)
- content_type = ContentType.objects.get_for_model(Question)
- self.__process_activities_badge(query, 2, Question)
-
- def delete_answer_be_voted_down_3(self):
- """
- (2, 'ååç½é¢', 3, 'ååç½é¢', 'å é¤èªå·±æ3个以ä¸å对票çå¸å', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM activity act, answer an WHERE act.object_id = an.id AND\
- act.activity_type = %s AND\
- an.vote_down_count >=3 AND \
- act.is_auditted = 0" % (TYPE_ACTIVITY_DELETE_ANSWER)
- self.__process_activities_badge(query, 2, Answer)
-
- def answer_be_voted_up_10(self):
- """
- (3, 'ä¼ç§åç', 3, 'ä¼ç§åç', 'åç好è¯10次以ä¸', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM \
- activity act, answer a WHERE act.object_id = a.id AND\
- act.activity_type = %s AND \
- a.vote_up_count >= 10 AND\
- act.is_auditted = 0" % (TYPE_ACTIVITY_ANSWER)
- self.__process_activities_badge(query, 3, Answer)
-
- def question_be_voted_up_10(self):
- """
- (4, 'ä¼ç§é®é¢', 3, 'ä¼ç§é®é¢', 'é®é¢å¥½è¯10次以ä¸', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM \
- activity act, question q WHERE act.object_id = q.id AND\
- act.activity_type = %s AND \
- q.vote_up_count >= 10 AND\
- act.is_auditted = 0" % (TYPE_ACTIVITY_ASK_QUESTION)
- self.__process_activities_badge(query, 4, Question)
-
- def question_view_1000(self):
- """
- (6, 'æµè¡é®é¢', 3, 'æµè¡é®é¢', 'é®é¢çæµè§éè¶
è¿1000人次', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM \
- activity act, question q WHERE act.activity_type = %s AND\
- act.object_id = q.id AND \
- q.view_count >= 1000 AND\
- act.object_id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 6)
- self.__process_activities_badge(query, 6, Question, False)
-
- def answer_self_question_be_voted_up_3(self):
- """
- (17, 'èªå¦ææ', 3, 'èªå¦ææ', 'åçèªå·±çé®é¢å¹¶ä¸æ3个以ä¸èµæ票', 1, 0),
- """
- query = "SELECT act.id, act.user_id, act.object_id FROM \
- activity act, answer an WHERE act.activity_type = %s AND\
- act.object_id = an.id AND\
- an.vote_up_count >= 3 AND\
- act.user_id = (SELECT user_id FROM question q WHERE q.id = an.question_id) AND\
- act.object_id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 17)
- self.__process_activities_badge(query, 17, Question, False)
-
- def answer_be_voted_up_100(self):
- """
- (18, 'ææä»·å¼åç', 1, 'ææä»·å¼åç', 'åçè¶
è¿100次èµæ票', 1, 0),
- """
- query = "SELECT an.id, an.author_id FROM answer an WHERE an.vote_up_count >= 100 AND an.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (18)
-
- self.__process_badge(query, 18, Answer)
-
- def question_be_voted_up_100(self):
- """
- (19, 'ææä»·å¼é®é¢', 1, 'ææä»·å¼é®é¢', 'é®é¢è¶
è¿100次èµæ票', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.vote_up_count >= 100 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (19)
-
- self.__process_badge(query, 19, Question)
-
- def question_be_favorited_100(self):
- """
- (20, 'ä¸äººè¿·', 1, 'ä¸äººè¿·', 'é®é¢è¢«100人以ä¸æ¶è', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.favourite_count >= 100 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (20)
-
- self.__process_badge(query, 20, Question)
-
- def question_view_10000(self):
- """
- (21, 'èåé®é¢', 1, 'èåé®é¢', 'é®é¢çæµè§éè¶
è¿10000人次', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.view_count >= 10000 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (21)
-
- self.__process_badge(query, 21, Question)
-
- def answer_be_voted_up_25(self):
- """
- (23, 'æ好åç', 2, 'æ好åç', 'åçè¶
è¿25次èµæ票', 1, 0),
- """
- query = "SELECT a.id, a.author_id FROM answer a WHERE a.vote_up_count >= 25 AND a.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (23)
-
- self.__process_badge(query, 23, Answer)
-
- def question_be_voted_up_25(self):
- """
- (24, 'æ好é®é¢', 2, 'æ好é®é¢', 'é®é¢è¶
è¿25次èµæ票', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.vote_up_count >= 25 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (24)
-
- self.__process_badge(query, 24, Question)
-
- def question_be_favorited_25(self):
- """
- (25, 'å欢è¿é®é¢', 2, 'å欢è¿é®é¢', 'é®é¢è¢«25人以ä¸æ¶è', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.favourite_count >= 25 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (25)
-
- self.__process_badge(query, 25, Question)
-
- def question_view_2500(self):
- """
- (31, 'æåå
³æ³¨é®é¢', 2, 'æåå
³æ³¨é®é¢', 'é®é¢çæµè§éè¶
è¿2500人次', 1, 0),
- """
- query = "SELECT q.id, q.author_id FROM question q WHERE q.view_count >= 2500 AND q.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (31)
-
- self.__process_badge(query, 31, Question)
-
- def answer_be_accepted_and_voted_up_40(self):
- """
- (34, '导å¸', 2, '导å¸', '被æå®ä¸ºæä½³çæ¡å¹¶ä¸èµæ票40以ä¸', 1, 0),
- """
- query = "SELECT a.id, a.author_id FROM answer a WHERE a.vote_up_count >= 40 AND\
- a.accepted = 1 AND\
- a.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (34)
-
- self.__process_badge(query, 34, Answer)
-
- def question_be_answered_after_60_days_and_be_voted_up_5(self):
- """
- (35, 'å·«å¸', 2, 'å·«å¸', 'å¨æé®60天ä¹ååç并ä¸èµæ票5次以ä¸', 1, 0),
- """
- query = "SELECT a.id, a.author_id FROM question q, answer a WHERE q.id = a.question_id AND\
- DATEDIFF(a.added_at, q.added_at) >= 60 AND\
- a.vote_up_count >= 5 AND \
- a.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (35)
-
- self.__process_badge(query, 35, Answer)
-
- def created_tag_be_used_in_question_50(self):
- """
- (36, 'åç±»ä¸å®¶', 2, 'åç±»ä¸å®¶', 'å建çæ ç¾è¢«50个以ä¸é®é¢ä½¿ç¨', 1, 0);
- """
- query = "SELECT t.id, t.created_by_id FROM tag t, auth_user u WHERE t.created_by_id = u.id AND \
- t. used_count >= 50 AND \
- t.id NOT IN \
- (SELECT object_id FROM award WHERE award.badge_id = %s)" % (36)
-
- self.__process_badge(query, 36, Tag)
-
- def __process_activities_badge(self, query, badge, content_object, update_auditted=True):
- content_type = ContentType.objects.get_for_model(content_object)
-
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- if update_auditted:
- activity_ids = []
- badge = get_object_or_404(Badge, id=badge)
- for row in rows:
- activity_id = row[0]
- user_id = row[1]
- object_id = row[2]
-
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
-
- if update_auditted:
- activity_ids.append(activity_id)
-
- if update_auditted:
- self.update_activities_auditted(cursor, activity_ids)
- finally:
- cursor.close()
-
- def __process_badge(self, query, badge, content_object):
- content_type = ContentType.objects.get_for_model(Answer)
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- badge = get_object_or_404(Badge, id=badge)
- for row in rows:
- object_id = row[0]
- user_id = row[1]
-
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
- finally:
- cursor.close()
diff --git a/forum/management/commands/once_award_badges.py b/forum/management/commands/once_award_badges.py
deleted file mode 100644
index 8c91334..0000000
--- a/forum/management/commands/once_award_badges.py
+++ /dev/null
@@ -1,350 +0,0 @@
-#!/usr/bin/env python
-#encoding:utf-8
-#-------------------------------------------------------------------------------
-# Name: Award badges command
-# Purpose: This is a command file croning in background process regularly to
-# query database and award badges for user's special acitivities.
-#
-# Author: Mike, Sailing
-#
-# Created: 18/01/2009
-# Copyright: (c) Mike 2009
-# Licence: GPL V2
-#-------------------------------------------------------------------------------
-
-from datetime import datetime, date
-from django.db import connection
-from django.shortcuts import get_object_or_404
-from django.contrib.contenttypes.models import ContentType
-
-from forum.models import *
-from forum.const import *
-from base_command import BaseCommand
-"""
-(1, 'ç¼ç±æ³å¸', 3, 'ç¼ç±æ³å¸', 'å é¤èªå·±æ3个以ä¸èµæ票çå¸å', 1, 0),
-(2, 'ååç½é¢', 3, 'ååç½é¢', 'å é¤èªå·±æ3个以ä¸å对票çå¸å', 1, 0),
-(3, 'ä¼ç§åç', 3, 'ä¼ç§åç', 'åç好è¯10次以ä¸', 1, 0),
-(4, 'ä¼ç§é®é¢', 3, 'ä¼ç§é®é¢', 'é®é¢å¥½è¯10次以ä¸', 1, 0),
-(5, 'è¯è®ºå®¶', 3, 'è¯è®ºå®¶', 'è¯è®º10次以ä¸', 0, 0),
-(6, 'æµè¡é®é¢', 3, 'æµè¡é®é¢', 'é®é¢çæµè§éè¶
è¿1000人次', 1, 0),
-(7, 'å·¡é»å
µ', 3, 'å·¡é»å
µ', '第ä¸æ¬¡æ è®°åå¾å¸å', 0, 0),
-(8, 'æ¸
æ´å·¥', 3, 'æ¸
æ´å·¥', '第ä¸æ¬¡æ¤éæ票', 0, 0),
-(9, 'æ¹è¯å®¶', 3, 'æ¹è¯å®¶', '第ä¸æ¬¡å对票', 0, 0),
-(10, 'å°ç¼', 3, 'å°ç¼', '第ä¸æ¬¡ç¼è¾æ´æ°', 0, 0),
-(11, 'æé¿', 3, 'æé¿', '第ä¸æ¬¡éæ°æ ç¾', 0, 0),
-(12, 'å¦è
', 3, 'å¦è
', '第ä¸æ¬¡æ è®°çæ¡', 0, 0),
-(13, 'å¦ç', 3, 'å¦ç', '第ä¸æ¬¡æé®å¹¶ä¸æä¸æ¬¡ä»¥ä¸èµæ票', 0, 0),
-(14, 'æ¯æè
', 3, 'æ¯æè
', '第ä¸æ¬¡èµæ票', 0, 0),
-(15, 'æå¸', 3, 'æå¸', '第ä¸æ¬¡åçé®é¢å¹¶ä¸å¾å°ä¸ä¸ªä»¥ä¸èµæ票', 0, 0),
-(16, 'èªä¼ ä½è
', 3, 'èªä¼ ä½è
', 'å®æ´å¡«åç¨æ·èµæææé项', 0, 0),
-(17, 'èªå¦ææ', 3, 'èªå¦ææ', 'åçèªå·±çé®é¢å¹¶ä¸æ3个以ä¸èµæ票', 1, 0),
-(18, 'ææä»·å¼åç', 1, 'ææä»·å¼åç', 'åçè¶
è¿100次èµæ票', 1, 0),
-(19, 'ææä»·å¼é®é¢', 1, 'ææä»·å¼é®é¢', 'é®é¢è¶
è¿100次èµæ票', 1, 0),
-(20, 'ä¸äººè¿·', 1, 'ä¸äººè¿·', 'é®é¢è¢«100人以ä¸æ¶è', 1, 0),
-(21, 'èåé®é¢', 1, 'èåé®é¢', 'é®é¢çæµè§éè¶
è¿10000人次', 1, 0),
-(22, 'alphaç¨æ·', 2, 'alphaç¨æ·', 'å
æµæé´çæ´»è·ç¨æ·', 0, 0),
-(23, 'æ好åç', 2, 'æ好åç', 'åçè¶
è¿25次èµæ票', 1, 0),
-(24, 'æ好é®é¢', 2, 'æ好é®é¢', 'é®é¢è¶
è¿25次èµæ票', 1, 0),
-(25, 'å欢è¿é®é¢', 2, 'å欢è¿é®é¢', 'é®é¢è¢«25人以ä¸æ¶è', 1, 0),
-(26, 'ä¼ç§å¸æ°', 2, 'ä¼ç§å¸æ°', 'æ票300次以ä¸', 0, 0),
-(27, 'ç¼è¾ä¸»ä»»', 2, 'ç¼è¾ä¸»ä»»', 'ç¼è¾äº100个å¸å', 0, 0),
-(28, 'éæ', 2, 'éæ', 'å¨å¤ä¸ªæ ç¾é¢åæ´»è·', 0, 0),
-(29, 'ä¸å®¶', 2, 'ä¸å®¶', 'å¨ä¸ä¸ªæ ç¾é¢åæ´»è·åºä¼', 0, 0),
-(30, 'èé¸', 2, 'èé¸', 'æ´»è·è¶
è¿ä¸å¹´çç¨æ·', 0, 0),
-(31, 'æåå
³æ³¨é®é¢', 2, 'æåå
³æ³¨é®é¢', 'é®é¢çæµè§éè¶
è¿2500人次', 1, 0),
-(32, 'å¦é®å®¶', 2, 'å¦é®å®¶', '第ä¸æ¬¡åç被æèµæ票10次以ä¸', 0, 0),
-(33, 'betaç¨æ·', 2, 'betaç¨æ·', 'betaæé´æ´»è·åä¸', 0, 0),
-(34, '导å¸', 2, '导å¸', '被æå®ä¸ºæä½³çæ¡å¹¶ä¸èµæ票40以ä¸', 1, 0),
-(35, 'å·«å¸', 2, 'å·«å¸', 'å¨æé®60天ä¹ååç并ä¸èµæ票5次以ä¸', 1, 0),
-(36, 'åç±»ä¸å®¶', 2, 'åç±»ä¸å®¶', 'å建çæ ç¾è¢«50个以ä¸é®é¢ä½¿ç¨', 1, 0);
-
-
-TYPE_ACTIVITY_ASK_QUESTION=1
-TYPE_ACTIVITY_ANSWER=2
-TYPE_ACTIVITY_COMMENT_QUESTION=3
-TYPE_ACTIVITY_COMMENT_ANSWER=4
-TYPE_ACTIVITY_UPDATE_QUESTION=5
-TYPE_ACTIVITY_UPDATE_ANSWER=6
-TYPE_ACTIVITY_PRIZE=7
-TYPE_ACTIVITY_MARK_ANSWER=8
-TYPE_ACTIVITY_VOTE_UP=9
-TYPE_ACTIVITY_VOTE_DOWN=10
-TYPE_ACTIVITY_CANCEL_VOTE=11
-TYPE_ACTIVITY_DELETE_QUESTION=12
-TYPE_ACTIVITY_DELETE_ANSWER=13
-TYPE_ACTIVITY_MARK_OFFENSIVE=14
-TYPE_ACTIVITY_UPDATE_TAGS=15
-TYPE_ACTIVITY_FAVORITE=16
-TYPE_ACTIVITY_USER_FULL_UPDATED = 17
-"""
-
-BADGE_AWARD_TYPE_FIRST = {
- TYPE_ACTIVITY_MARK_OFFENSIVE : 7,
- TYPE_ACTIVITY_CANCEL_VOTE: 8,
- TYPE_ACTIVITY_VOTE_DOWN : 9,
- TYPE_ACTIVITY_UPDATE_QUESTION : 10,
- TYPE_ACTIVITY_UPDATE_ANSWER : 10,
- TYPE_ACTIVITY_UPDATE_TAGS : 11,
- TYPE_ACTIVITY_MARK_ANSWER : 12,
- TYPE_ACTIVITY_VOTE_UP : 14,
- TYPE_ACTIVITY_USER_FULL_UPDATED: 16
-
-}
-
-class Command(BaseCommand):
- def handle_noargs(self, **options):
- try:
- try:
- self.alpha_user()
- self.beta_user()
- self.first_type_award()
- self.first_ask_be_voted()
- self.first_answer_be_voted()
- self.first_answer_be_voted_10()
- self.vote_count_300()
- self.edit_count_100()
- self.comment_count_10()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def alpha_user(self):
- """
- Before Jan 25, 2009(Chinese New Year Eve and enter into Beta for CNProg), every registered user
- will be awarded the "Alpha" badge if he has any activities.
- """
- alpha_end_date = date(2009, 1, 25)
- if date.today() < alpha_end_date:
- badge = get_object_or_404(Badge, id=22)
- for user in User.objects.all():
- award = Award.objects.filter(user=user, badge=badge)
- if award and not badge.multiple:
- continue
- activities = Activity.objects.filter(user=user)
- if len(activities) > 0:
- new_award = Award(user=user, badge=badge)
- new_award.save()
-
- def beta_user(self):
- """
- Before Feb 25, 2009, every registered user
- will be awarded the "Beta" badge if he has any activities.
- """
- beta_end_date = date(2009, 2, 25)
- if date.today() < beta_end_date:
- badge = get_object_or_404(Badge, id=33)
- for user in User.objects.all():
- award = Award.objects.filter(user=user, badge=badge)
- if award and not badge.multiple:
- continue
- activities = Activity.objects.filter(user=user)
- if len(activities) > 0:
- new_award = Award(user=user, badge=badge)
- new_award.save()
-
- def first_type_award(self):
- """
- This will award below badges for users first behaviors:
-
- (7, 'å·¡é»å
µ', 3, 'å·¡é»å
µ', '第ä¸æ¬¡æ è®°åå¾å¸å', 0, 0),
- (8, 'æ¸
æ´å·¥', 3, 'æ¸
æ´å·¥', '第ä¸æ¬¡æ¤éæ票', 0, 0),
- (9, 'æ¹è¯å®¶', 3, 'æ¹è¯å®¶', '第ä¸æ¬¡å对票', 0, 0),
- (10, 'å°ç¼', 3, 'å°ç¼', '第ä¸æ¬¡ç¼è¾æ´æ°', 0, 0),
- (11, 'æé¿', 3, 'æé¿', '第ä¸æ¬¡éæ°æ ç¾', 0, 0),
- (12, 'å¦è
', 3, 'å¦è
', '第ä¸æ¬¡æ è®°çæ¡', 0, 0),
- (14, 'æ¯æè
', 3, 'æ¯æè
', '第ä¸æ¬¡èµæ票', 0, 0),
- (16, 'èªä¼ ä½è
', 3, 'èªä¼ ä½è
', 'å®æ´å¡«åç¨æ·èµæææé项', 0, 0),
- """
- activity_types = ','.join('%s' % item for item in BADGE_AWARD_TYPE_FIRST.keys())
- # ORDER BY user_id, activity_type
- query = "SELECT id, user_id, activity_type, content_type_id, object_id FROM activity WHERE is_auditted = 0 AND activity_type IN (%s) ORDER BY user_id, activity_type" % activity_types
-
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
- # collect activity_id in current process
- activity_ids = []
- last_user_id = 0
- last_activity_type = 0
- for row in rows:
- activity_ids.append(row[0])
- user_id = row[1]
- activity_type = row[2]
- content_type_id = row[3]
- object_id = row[4]
-
- # if the user and activity are same as the last, continue
- if user_id == last_user_id and activity_type == last_activity_type:
- continue;
-
- user = get_object_or_404(User, id=user_id)
- badge = get_object_or_404(Badge, id=BADGE_AWARD_TYPE_FIRST[activity_type])
- content_type = get_object_or_404(ContentType, id=content_type_id)
-
- count = Award.objects.filter(user=user, badge=badge).count()
- if count and not badge.multiple:
- continue
- else:
- # new award
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
-
- # set the current user_id and activity_type to last
- last_user_id = user_id
- last_activity_type = activity_type
-
- # update processed rows to auditted
- self.update_activities_auditted(cursor, activity_ids)
- finally:
- cursor.close()
-
- def first_ask_be_voted(self):
- """
- For user asked question and got first upvote, we award him following badge:
-
- (13, 'å¦ç', 3, 'å¦ç', '第ä¸æ¬¡æé®å¹¶ä¸æä¸æ¬¡ä»¥ä¸èµæ票', 0, 0),
- """
- query = "SELECT act.user_id, q.vote_up_count, act.object_id FROM " \
- "activity act, question q WHERE act.activity_type = %s AND " \
- "act.object_id = q.id AND " \
- "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ASK_QUESTION, 13)
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- badge = get_object_or_404(Badge, id=13)
- content_type = ContentType.objects.get_for_model(Question)
- awarded_users = []
- for row in rows:
- user_id = row[0]
- vote_up_count = row[1]
- object_id = row[2]
- if vote_up_count > 0 and user_id not in awarded_users:
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
- awarded_users.append(user_id)
- finally:
- cursor.close()
-
- def first_answer_be_voted(self):
- """
- When user answerd questions and got first upvote, we award him following badge:
-
- (15, 'æå¸', 3, 'æå¸', '第ä¸æ¬¡åçé®é¢å¹¶ä¸å¾å°ä¸ä¸ªä»¥ä¸èµæ票', 0, 0),
- """
- query = "SELECT act.user_id, a.vote_up_count, act.object_id FROM " \
- "activity act, answer a WHERE act.activity_type = %s AND " \
- "act.object_id = a.id AND " \
- "act.user_id NOT IN (SELECT distinct user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 15)
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- awarded_users = []
- badge = get_object_or_404(Badge, id=15)
- content_type = ContentType.objects.get_for_model(Answer)
- for row in rows:
- user_id = row[0]
- vote_up_count = row[1]
- object_id = row[2]
- if vote_up_count > 0 and user_id not in awarded_users:
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
- awarded_users.append(user_id)
- finally:
- cursor.close()
-
- def first_answer_be_voted_10(self):
- """
- (32, 'å¦é®å®¶', 2, 'å¦é®å®¶', '第ä¸æ¬¡åç被æèµæ票10次以ä¸', 0, 0)
- """
- query = "SELECT act.user_id, act.object_id FROM " \
- "activity act, answer a WHERE act.object_id = a.id AND " \
- "act.activity_type = %s AND " \
- "a.vote_up_count >= 10 AND " \
- "act.user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s)" % (TYPE_ACTIVITY_ANSWER, 32)
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- awarded_users = []
- badge = get_object_or_404(Badge, id=32)
- content_type = ContentType.objects.get_for_model(Answer)
- for row in rows:
- user_id = row[0]
- if user_id not in awarded_users:
- user = get_object_or_404(User, id=user_id)
- object_id = row[1]
- award = Award(user=user, badge=badge, content_type=content_type, object_id=object_id)
- award.save()
- awarded_users.append(user_id)
- finally:
- cursor.close()
-
- def vote_count_300(self):
- """
- (26, 'ä¼ç§å¸æ°', 2, 'ä¼ç§å¸æ°', 'æ票300次以ä¸', 0, 0)
- """
- query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
- "activity_type = %s OR " \
- "activity_type = %s AND " \
- "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
- "GROUP BY user_id HAVING vote_count >= 300" % (TYPE_ACTIVITY_VOTE_UP, TYPE_ACTIVITY_VOTE_DOWN, 26)
-
- self.__award_for_count_num(query, 26)
-
- def edit_count_100(self):
- """
- (27, 'ç¼è¾ä¸»ä»»', 2, 'ç¼è¾ä¸»ä»»', 'ç¼è¾äº100个å¸å', 0, 0)
- """
- query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
- "activity_type = %s OR " \
- "activity_type = %s AND " \
- "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
- "GROUP BY user_id HAVING vote_count >= 100" % (TYPE_ACTIVITY_UPDATE_QUESTION, TYPE_ACTIVITY_UPDATE_ANSWER, 27)
-
- self.__award_for_count_num(query, 27)
-
- def comment_count_10(self):
- """
- (5, 'è¯è®ºå®¶', 3, 'è¯è®ºå®¶', 'è¯è®º10次以ä¸', 0, 0),
- """
- query = "SELECT count(*) vote_count, user_id FROM activity WHERE " \
- "activity_type = %s OR " \
- "activity_type = %s AND " \
- "user_id NOT IN (SELECT user_id FROM award WHERE badge_id = %s) " \
- "GROUP BY user_id HAVING vote_count >= 10" % (TYPE_ACTIVITY_COMMENT_QUESTION, TYPE_ACTIVITY_COMMENT_ANSWER, 5)
- self.__award_for_count_num(query, 5)
-
- def __award_for_count_num(self, query, badge):
- cursor = connection.cursor()
- try:
- cursor.execute(query)
- rows = cursor.fetchall()
-
- awarded_users = []
- badge = get_object_or_404(Badge, id=badge)
- for row in rows:
- vote_count = row[0]
- user_id = row[1]
-
- if user_id not in awarded_users:
- user = get_object_or_404(User, id=user_id)
- award = Award(user=user, badge=badge)
- award.save()
- awarded_users.append(user_id)
- finally:
- cursor.close()
-
-def main():
- pass
-
-if __name__ == '__main__':
- main()
diff --git a/forum/management/commands/sample_command.py b/forum/management/commands/sample_command.py
deleted file mode 100644
index 55e6723..0000000
--- a/forum/management/commands/sample_command.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.core.management.base import NoArgsCommand
-from forum.models import Comment
-
-class Command(NoArgsCommand):
- def handle_noargs(self, **options):
- objs = Comment.objects.all()
- print objs
\ No newline at end of file
diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py
deleted file mode 100644
index 26eb779..0000000
--- a/forum/management/commands/send_email_alerts.py
+++ /dev/null
@@ -1,192 +0,0 @@
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.db.models import Q, F
-from forum.models import *
-from forum import const
-from django.core.mail import EmailMessage
-from django.utils.translation import ugettext as _
-from django.utils.translation import ungettext
-import datetime
-from django.conf import settings
-import logging
-from forum.utils.odict import OrderedDict
-
-class Command(NoArgsCommand):
- def handle_noargs(self,**options):
- try:
- try:
- self.send_email_alerts()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def get_updated_questions_for_user(self,user):
- q_sel = None
- q_ask = None
- q_ans = None
- q_all = None
- now = datetime.datetime.now()
- Q_set1 = Question.objects.exclude(
- last_activity_by=user,
- ).exclude(
- last_activity_at__lt=user.date_joined
- ).filter(
- Q(viewed__who=user,viewed__when__lt=F('last_activity_at')) | \
- ~Q(viewed__who=user)
- ).exclude(
- deleted=True
- ).exclude(
- closed=True
- )
-
- user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude(frequency='n')
- for feed in user_feeds:
- cutoff_time = now - EmailFeedSetting.DELTA_TABLE[feed.frequency]
- if feed.reported_at == None or feed.reported_at <= cutoff_time:
- Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later
- feed.reported_at = now
- feed.save()#may not actually report anything, depending on filters below
- if feed.feed_type == 'q_sel':
- q_sel = Q_set.filter(followed_by=user)
- q_sel.cutoff_time = cutoff_time #store cutoff time per query set
- elif feed.feed_type == 'q_ask':
- q_ask = Q_set.filter(author=user)
- q_ask.cutoff_time = cutoff_time
- elif feed.feed_type == 'q_ans':
- q_ans = Q_set.filter(answers__author=user)
- q_ans.cutoff_time = cutoff_time
- elif feed.feed_type == 'q_all':
- if user.tag_filter_setting == 'ignored':
- ignored_tags = Tag.objects.filter(user_selections__reason='bad',user_selections__user=user)
- q_all = Q_set.exclude( tags__in=ignored_tags )
- else:
- selected_tags = Tag.objects.filter(user_selections__reason='good',user_selections__user=user)
- q_all = Q_set.filter( tags__in=selected_tags )
- q_all.cutoff_time = cutoff_time
- #build list in this order
- q_list = OrderedDict()
- def extend_question_list(src, dst):
- """src is a query set with questions
- or an empty list
- dst - is an ordered dictionary
- """
- if src is None:
- return #will not do anything if subscription of this type is not used
- cutoff_time = src.cutoff_time
- for q in src:
- if q in dst:
- if cutoff_time < dst[q]['cutoff_time']:
- dst[q]['cutoff_time'] = cutoff_time
- else:
- #initialise a questions metadata dictionary to use for email reporting
- dst[q] = {'cutoff_time':cutoff_time}
-
- extend_question_list(q_sel, q_list)
- extend_question_list(q_ask, q_list)
- extend_question_list(q_ans, q_list)
- extend_question_list(q_all, q_list)
-
- ctype = ContentType.objects.get_for_model(Question)
- EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT
- for q, meta_data in q_list.items():
- #todo use Activity, but first start keeping more Activity records
- #act = Activity.objects.filter(content_type=ctype, object_id=q.id)
- #because currently activity is not fully recorded to through
- #revision records to see what kind modifications were done on
- #the questions and answers
- try:
- update_info = Activity.objects.get(content_type=ctype,
- object_id=q.id,
- activity_type=EMAIL_UPDATE_ACTIVITY)
- emailed_at = update_info.active_at
- except Activity.DoesNotExist:
- update_info = Activity(user=user, content_object=q, activity_type=EMAIL_UPDATE_ACTIVITY)
- emailed_at = datetime.datetime(1970,1,1)#long time ago
- except Activity.MultipleObjectsReturned:
- raise Exception('server error - multiple question email activities found per user-question pair')
-
- q_rev = QuestionRevision.objects.filter(question=q,\
- revised_at__lt=cutoff_time,\
- revised_at__gt=emailed_at)
- q_rev = q_rev.exclude(author=user)
- meta_data['q_rev'] = len(q_rev)
- if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at:
- meta_data['q_rev'] = 0
- meta_data['new_q'] = True
- else:
- meta_data['new_q'] = False
-
- new_ans = Answer.objects.filter(question=q,\
- added_at__lt=cutoff_time,\
- added_at__gt=emailed_at)
- new_ans = new_ans.exclude(author=user)
- meta_data['new_ans'] = len(new_ans)
- ans_rev = AnswerRevision.objects.filter(answer__question=q,\
- revised_at__lt=cutoff_time,\
- revised_at__gt=emailed_at)
- ans_rev = ans_rev.exclude(author=user)
- meta_data['ans_rev'] = len(ans_rev)
- if len(q_rev) == 0 and len(new_ans) == 0 and len(ans_rev) == 0:
- meta_data['nothing_new'] = True
- else:
- meta_data['nothing_new'] = False
- update_info.active_at = now
- update_info.save() #save question email update activity
- return q_list
-
- def __action_count(self,string,number,output):
- if number > 0:
- 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)
- num_q = 0
- num_moot = 0
- for meta_data in q_list.values():
- if meta_data['nothing_new'] == False:
- num_q += 1
- else:
- num_moot += 1
- if num_q > 0:
- url_prefix = settings.APP_URL
- subject = _('email update message subject')
- print 'have %d updated questions for %s' % (num_q, user.username)
- text = ungettext('%(name)s, this is an update message header for a question',
- '%(name)s, this is an update message header for %(num)d questions',num_q) \
- % {'num':num_q, 'name':user.username}
-
- text += ''
- for q, meta_data in q_list.items():
- act_list = []
- if meta_data['nothing_new']:
- continue
- else:
- if meta_data['new_q']:
- act_list.append(_('new question'))
- self.__action_count('%(num)d rev', meta_data['q_rev'],act_list)
- self.__action_count('%(num)d ans', meta_data['new_ans'],act_list)
- self.__action_count('%(num)d ans rev',meta_data['ans_rev'],act_list)
- act_token = ', '.join(act_list)
- text += '- %s (%s)
' \
- % (url_prefix + q.get_absolute_url(), q.title, act_token)
- text += '
'
- if num_moot > 0:
- text += ''
- text += ungettext('There is also one question which was recently '\
- +'updated but you might not have seen its latest version.',
- 'There are also %(num)d more questions which were recently updated '\
- +'but you might not have seen their latest version.',num_moot) \
- % {'num':num_moot,}
- text += _('Perhaps you could look up previously sent forum reminders in your mailbox.')
- text += '
'
-
- link = url_prefix + user.get_profile_url() + '?sort=email_subscriptions'
- text += _('go to %(link)s to change frequency of email updates or %(email)s administrator') \
- % {'link':link, 'email':settings.ADMINS[0][1]}
- msg = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, [user.email])
- msg.content_subtype = 'html'
- msg.send()
diff --git a/forum/management/commands/subscribe_everyone.py b/forum/management/commands/subscribe_everyone.py
deleted file mode 100644
index c79528f..0000000
--- a/forum/management/commands/subscribe_everyone.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from django.core.management.base import NoArgsCommand
-from django.db import connection
-from django.db.models import Q, F
-from forum.models import *
-from django.core.mail import EmailMessage
-from django.utils.translation import ugettext as _
-from django.utils.translation import ungettext
-import datetime
-from django.conf import settings
-
-class Command(NoArgsCommand):
- def handle_noargs(self,**options):
- try:
- try:
- self.subscribe_everyone()
- except Exception, e:
- print e
- finally:
- connection.close()
-
- def subscribe_everyone(self):
-
- feed_type_info = EmailFeedSetting.FEED_TYPES
- for user in User.objects.all():
- for feed_type in feed_type_info:
- try:
- feed_setting = EmailFeedSetting.objects.get(subscriber=user,feed_type = feed_type[0])
- except EmailFeedSetting.DoesNotExist:
- feed_setting = EmailFeedSetting(subscriber=user,feed_type=feed_type[0])
- feed_setting.frequency = 'w'
- feed_setting.reported_at = None
- feed_setting.save()
diff --git a/forum/middleware/__init__.py b/forum/middleware/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/forum/middleware/anon_user.py b/forum/middleware/anon_user.py
deleted file mode 100644
index 866734d..0000000
--- a/forum/middleware/anon_user.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from django.http import HttpResponseRedirect
-from forum.utils.forms import get_next_url
-from django.utils.translation import ugettext as _
-from forum.user_messages import create_message, get_and_delete_messages
-from django.conf import settings
-from django.core.urlresolvers import reverse
-import logging
-
-class AnonymousMessageManager(object):
- def __init__(self,request):
- self.request = request
- def create(self,message=''):
- create_message(self.request,message)
- def get_and_delete(self):
- messages = get_and_delete_messages(self.request)
- return messages
-
-def dummy_deepcopy(*arg):
- """this is necessary to prevent deepcopy() on anonymous user object
- that now contains reference to request, which cannot be deepcopied
- """
- return None
-
-class ConnectToSessionMessagesMiddleware(object):
- def process_request(self, request):
- if not request.user.is_authenticated():
- request.user.__deepcopy__ = dummy_deepcopy #plug on deepcopy which may be called by django db "driver"
- request.user.message_set = AnonymousMessageManager(request) #here request is linked to anon user
- request.user.get_and_delete_messages = request.user.message_set.get_and_delete
-
- #also set the first greeting one time per session only
- if 'greeting_set' not in request.session:
- request.session['greeting_set'] = True
- msg = _('First time here? Check out the FAQ!') % reverse('faq')
- request.user.message_set.create(message=msg)
diff --git a/forum/middleware/cancel.py b/forum/middleware/cancel.py
deleted file mode 100644
index 15a4371..0000000
--- a/forum/middleware/cancel.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from django.http import HttpResponseRedirect
-from forum.utils.forms import get_next_url
-import logging
-class CancelActionMiddleware(object):
- def process_view(self, request, view_func, view_args, view_kwargs):
- if 'cancel' in request.REQUEST:
- #todo use session messages for the anonymous users
- try:
- msg = getattr(view_func,'CANCEL_MESSAGE')
- except AttributeError:
- msg = 'action canceled'
- request.user.message_set.create(message=msg)
- return HttpResponseRedirect(get_next_url(request))
- else:
- return None
diff --git a/forum/middleware/pagesize.py b/forum/middleware/pagesize.py
deleted file mode 100644
index f6e6fcf..0000000
--- a/forum/middleware/pagesize.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# used in questions
-QUESTIONS_PAGE_SIZE = 10
-class QuestionsPageSizeMiddleware(object):
- def process_request(self, request):
- # 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:
- # 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:
- pagesize = user_page_size
-
- # save this pagesize to user database
- if pagesize_changed:
- if request.user.is_authenticated():
- user = request.user
- user.questions_per_page = pagesize
- user.save()
- # put pagesize into session
- request.session["pagesize"] = pagesize
-
- def process_exception(self,request,exception):
- import logging
- logging.debug('have exception %s' % str(exception))
diff --git a/forum/models/__init__.py b/forum/models/__init__.py
deleted file mode 100755
index 12a0239..0000000
--- a/forum/models/__init__.py
+++ /dev/null
@@ -1,343 +0,0 @@
-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, AuthKeyUserAssociation
-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'%s' % (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
-AuthKeyUserAssociation = AuthKeyUserAssociation
-
-__all__ = [
- 'Question',
- 'QuestionRevision',
- 'QuestionView',
- 'FavoriteQuestion',
- 'AnonymousQuestion',
-
- 'Answer',
- 'AnswerRevision',
- 'AnonymousAnswer',
-
- 'Tag',
- 'Comment',
- 'Vote',
- 'FlaggedItem',
- 'MarkedTag',
-
- 'Badge',
- 'Award',
- 'Repute',
-
- 'Activity',
- 'EmailFeedSetting',
- 'AnonymousEmail',
- 'AuthKeyUserAssociation',
-
- '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
deleted file mode 100755
index 14199de..0000000
--- a/forum/models/answer.py
+++ /dev/null
@@ -1,134 +0,0 @@
-from base import *
-
-from question import Question
-
-class AnswerManager(models.Manager):
- @staticmethod
- def create_new(cls, 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
deleted file mode 100755
index 2c28a47..0000000
--- a/forum/models/base.py
+++ /dev/null
@@ -1,139 +0,0 @@
-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
deleted file mode 100755
index 3dfd3e8..0000000
--- a/forum/models/meta.py
+++ /dev/null
@@ -1,89 +0,0 @@
-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
deleted file mode 100755
index f916e65..0000000
--- a/forum/models/question.py
+++ /dev/null
@@ -1,336 +0,0 @@
-from base import *
-from tag import Tag
-
-class QuestionManager(models.Manager):
- @staticmethod
- def create_new(cls, title=None,author=None,added_at=None, wiki=False,tagnames=None,summary=None, text=None):
- question = Question(
- title = title,
- author = author,
- 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 = '%s:
\n' % (url,self.title)
- out = map(lambda x: '' + x + '',out)
- retval += '
\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
deleted file mode 100755
index a47ce47..0000000
--- a/forum/models/repute.py
+++ /dev/null
@@ -1,109 +0,0 @@
-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'
diff --git a/forum/models/tag.py b/forum/models/tag.py
deleted file mode 100755
index 28b9e57..0000000
--- a/forum/models/tag.py
+++ /dev/null
@@ -1,85 +0,0 @@
-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
deleted file mode 100755
index 9502416..0000000
--- a/forum/models/user.py
+++ /dev/null
@@ -1,77 +0,0 @@
-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'
-
-
-class AuthKeyUserAssociation(models.Model):
- key = models.CharField(max_length=256,null=False,unique=True)
- provider = models.CharField(max_length=64)
- user = models.ForeignKey(User)
- added_at = models.DateTimeField(default=datetime.datetime.now)
-
- class Meta:
- app_label = 'forum'
-
-
\ No newline at end of file
diff --git a/forum/modules.py b/forum/modules.py
deleted file mode 100755
index 9c07233..0000000
--- a/forum/modules.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import os
-import types
-import re
-
-from django.template import Template, TemplateDoesNotExist
-
-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 Exception, e:
- #print script_name + ":" + str(e)
- 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
-
-module_template_re = re.compile('^modules\/(\w+)\/(.*)$')
-
-def module_templates_loader(name, dirs=None):
- result = module_template_re.search(name)
-
- if result is not None:
- file_name = os.path.join(MODULES_FOLDER, result.group(1), 'templates', result.group(2))
-
- if os.path.exists(file_name):
- try:
- f = open(file_name, 'r')
- source = f.read()
- f.close()
- return (source, file_name)
- except:
- pass
-
- raise TemplateDoesNotExist, name
-
-module_templates_loader.is_usable = True
\ No newline at end of file
diff --git a/forum/sitemap.py b/forum/sitemap.py
deleted file mode 100644
index c0c60b5..0000000
--- a/forum/sitemap.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from django.contrib.sitemaps import Sitemap
-from forum.models import Question
-
-class QuestionsSitemap(Sitemap):
- changefreq = 'daily'
- priority = 0.5
- def items(self):
- return Question.objects.exclude(deleted=True)
-
- def lastmod(self, obj):
- return obj.last_activity_at
-
- def location(self, obj):
- return obj.get_absolute_url()
diff --git a/forum/skins/README b/forum/skins/README
deleted file mode 100644
index 5565fa8..0000000
--- a/forum/skins/README
+++ /dev/null
@@ -1,22 +0,0 @@
-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
deleted file mode 100644
index be6bd4f..0000000
--- a/forum/skins/__init__.py
+++ /dev/null
@@ -1,57 +0,0 @@
-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
deleted file mode 100644
index 3376e75..0000000
--- a/forum/skins/common/media/README
+++ /dev/null
@@ -1 +0,0 @@
-directory for media common to all or many templates
diff --git a/forum/skins/default/media/images/blue-up-arrow-h18px.png b/forum/skins/default/media/images/blue-up-arrow-h18px.png
deleted file mode 100644
index e1f29e86334ce72d2d28989a133571d7bf53a94e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 593
zcmV-X0{6iqad+$V|KR(Q{q;Ok;ua!jjQtuI1z7)O(
zySE1i5k-TnUMhU^!Fth&L`=(4hnCB&mew0>atrF^~@9muPo$nmJ5BNLwb>CS+
z;4aaxR`*WD1Hfmk^?b25_^vW#;gMP0a=;-lw8uKYF+TucXlk2wNsBS@}pFD
ztaavBN$@-;DyO5Dqi0!d9a~=U7;O0Ayiuq>#Jyi<@Hb@35~1ra$v5dmgDzqB>^+=uOM7b5+>fY}+kwy1R`cV__*wFuQ!Bi@#VE~BJd
f7UnWh{5Sjn&bP0xmAf5500000NkvXXu0mjfU!M$-
diff --git a/forum/skins/default/media/images/box-arrow.gif b/forum/skins/default/media/images/box-arrow.gif
deleted file mode 100644
index 89dcf5b3dd40fac0e6afb0b1a7ff899a059f923f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 69
zcmZ?wbhEHbWM@!dXkcJ?_wL=lfBzJJvM@6+Ff!;c00Bsbfk~#Pf92`7{EJz*0#eqW
WoE7iinDij`X5pe+YFj-S8LR>Nkr%B1
diff --git a/forum/skins/default/media/images/bullet_green.gif b/forum/skins/default/media/images/bullet_green.gif
deleted file mode 100644
index fa530910f9dc11fadaa2314f72bd98f29df39daf..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 64
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
D3>(Ui
diff --git a/forum/skins/default/media/images/close-small-dark.png b/forum/skins/default/media/images/close-small-dark.png
deleted file mode 100644
index 280c1fc74e47c0e7d1c68d6f356eb22eeba7a2de..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 226
zcmeAS@N?(olHy`uVBq!ia0vp^d?3ui3?$#C89V|~1_3@Hu3NY7ShRS_jG43c?mKYe
z;M6!dxb8-^+kP+MX_sAsp9}6ArM>V3CjzZ7^A^)I5Q~
t!yzC*Q0L$Zl?Eqn$0Lca5`Jx9W=NmTc;}pLNdZtBgQu&X%Q~loCIHsxKFr1QCT0q{1{MYeDE)e-c@Ne1ia=5ZCX&{{Hy&_tW!N_ZKfc
zpObUM#pTWAs~3xk4w#ty`t$F>lBGXhy}DLgdpr1Q#zqRd1{MYe;g^zIfZEtgJbhi+Z*Z~+S_nO6;N1-r
z;wE)e-c@Ne1ia=5Z4(qXDwR1Wb5`Fd-okU
zaq{%lYd1c8`uyw9U!Xizkhgxu4WI-^NswRge+Xc>*St3pD5mS_;uunKD_NtRfsJRE
z&YnFcvL4forc1XpAK{H{IrvG{>z#&4h|Ds+W&FE$GCZx(SSr0JY6i#_Pgg&ebxsLQ
E08gA+X#fBK
diff --git a/forum/skins/default/media/images/dash.gif b/forum/skins/default/media/images/dash.gif
deleted file mode 100644
index d1ddc507fe00bd654fce38ac8552793aa18c9966..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 44
wcmZ?wbhEHbWMN=rXkcLY|NsBAY10&cvM{hS{AbW%00NK<0~50k11p0y023w&(*OVf
diff --git a/forum/skins/default/media/images/djangomade124x25_grey.gif b/forum/skins/default/media/images/djangomade124x25_grey.gif
deleted file mode 100644
index d34bb311615b1378a672a828c7a7916490cd882b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2035
zcmcJOSyz(>0zk8Y5oHk(5d(s_FbFhAqNt4L16&YNCyID%iyA#v9i~-V)T#$7C{?Rq
zJuXmNkX;Zk3t36XzAs-E$W8(YK|~TDELE}gFw-9~_w_#9r@MFGo{ViLN+D5@_t6k?
zub_>}Y9`ZKyBS?Q+zuw^CB28p=9m6l=^h>P&(87W>M^fRs?m>(j#(W;9ErkUvGq!o
zR>z=aV9@3qVhSZRzIguQM;cEA4PH>{`)!UPyVLdOr%&oW6O}KX^iRvR21>7B_K!bg
zpuy#NEl_HdeI}XK;2M6dGg;OB7NH7sx`ycjiOOJXqcVp_-!Mh8P9}%M;d7*l9dn^o^Yc8JiXo6#2L{zfi^DzK#p>;|ITX6Se!BxS
zS=nOw+`D&V4qvF$^jqyzo~TDG>y;|lVi{d1<;qn?yF=A))|stRQ2&y_<|)*2U7tjw
zV~V8YUSW?!VYCi#Byx?(%9SZ)x_-xyo5JBc-5#D?H9GEn-bvP4>_a1?Mw^2xQ-LN+
zBe7ec(5U;2FS?oT(KkAyXb
zH`<+SiGsov4vvg6M6xClb-?9rrO;i&BX+lk$mV$`rZ`gN>v8YfiAm6G<101ow4OKP
zUbVqQ?BTjSuXPq%Yd5pISJ261cQQEjo#Yk@y{?1ASAq*s-`cubDGW-lki_O|%{D4etT9^j7JEC5N#je{GL^~hpbMnVVNY!b
z@p%VHZ?SpC-cHTTm>h#mB$`;Qbq9D5VJ`EU_H44c4@Fu%*g{g$2m}
zjre&46atBcB>!Fb`w0kg0fIg2-$QN?-a;-U9a~C|Sx_l#$$
zbyds`pGKe0LOZIh;hTXQH~7?2Jo0
zAKTtHp2*){xDdLxDX)5Bq$d9S)rRYTt~R1BZu8%arq9oL7S0X{cVZ83Md|CWuc5Ww
zgHJrm4n{311;Egli>`(nMilUQIqb4)5Q?cl#}=HzL03S&F5J5ZvF3I&1iAmphxib7
z=~(%y)r?(&s;zbaOiB%VcyqB)a3LW*6`!N2T5$*vtPDAu4Lr&i{1t{hUIbJv$v&)4
zc!CfB!4I<5zFha<24-Hg2z@rDMTRf&8K%(xp|Jl;8yyb7(z29dO~7
z0?>d2d_u(#9&?g)BczhRg5UXUk{R}HU+hrKk846$t{)2uvfo5}`X_+FT_r3*q@K(Q
zIFy3^Nem&SdwmdC?f@$3a69--AQ=DME>cj0|A&0oy86<nZyuyLVuwr{IGXQux+DRXY@2U6(<)cxduLt8LR@#Wa`O=WAre>^=26DJK^^h~Zenh_Qpc_eE|a>K3SC}>hGS%%baA>Rzm6~eB6s0c`oTN0US
zfK?FULsI-Xr|IOSn-WhZ6RhFiUYWj6SlS#V&y{WdZMyPn->wo=`JS}ytYDdcZ9-8+
z#g^jc^%g~Q?0bcL=f~+^%9ggo{bjwMjFH13QK!~qEei?la)%F0L$*X*t*SoddGxEM
zWy4mba{slN#|^%bl*nZdHaOy*)Xwd`S5&UbXN;^SSGFdf*{A-}K2y!otWP}=A9U0{
sv!XO(598i6?6_>^sSNjGbB$7Sdi(Nfq+XETwht`+l3E>&L_&7_7sn(m+5i9m
diff --git a/forum/skins/default/media/images/dot-g.gif b/forum/skins/default/media/images/dot-g.gif
deleted file mode 100644
index 5d6bb28e56377b0eeb80899222aa3290ec2a3a95..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 61
zcmZ?wbhEHb1;oomohA-cw8MruKF$f7>W?=Zo!hivofRLHNli}wd9fr@0yBS{oImf`r!T=CJ
zObo<=KYvmfSa>QJq}1IQ{xNkh{QGMS5(Wq$!Uiw`?fv_wh=H9yhCxi_BLl18R|YAy
zzYGlQ%?$tlTLT0TVFP~usbb(1@&ju6$nf>=e}=c;7#aR?|7H-C1iI?46+i&7;IWyL
z;n$xU20l?+1~CPo=06M!Uw{_>X8zCc{xt`~yO&=Xm{>Lg1Q0F*xPh8m83e@j8ARki
zGJO8U!0_!K1H&(%gWtd9WO)AQ6T^RoY6eEe8vp@>#en~UKxelz2uZ60@n?olzZn_6
z{|9LXI{!5n!^+`mIW?f>FASf4GBSJvI{OzR
z1H&<(HAFtjse0v3qvG*Lj!k<6yW@2D$#+OV00*L7y
zBZtO!W_$i{sO4^#Rs-O?>`EBV-VT&i$$;vm@)9CGk^eM*?-}6i0Jdne+|V2
zxHvw3_{Z?+(@RDM_T7w3%nJ!=1_6KoVp+N8l7OGV`-jpW1oz1C@m=Tm^ZxZW7V#J0
oz$F3z1Q6r@|Nj{%0RRC80Nya-9-e3d82|tP07*qoM6N<$g1t^w0{{R3
diff --git a/forum/skins/default/media/images/expander-arrow-hide.gif b/forum/skins/default/media/images/expander-arrow-hide.gif
deleted file mode 100644
index feb6a6187c2742ea8e516244f139e7946ed757fb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 126
zcmZ?wbhEHb6k!l$Xl7tYOG_C^&ZP*n$NM9zTBE+}u2E+O#ug&iw!Xp8*?C{K>-1
zz`((v1Cj)p!N8&|aME-2UW?afcmGe&$YV~Jr(qS6`f6R+>scREmz$PdzB_la&4mZ%
LT2lR!I2fz}g2F4-
diff --git a/forum/skins/default/media/images/expander-arrow-show.gif b/forum/skins/default/media/images/expander-arrow-show.gif
deleted file mode 100644
index 6825c56ee42f0184d66c0fe954d7fc4b6f05e850..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 135
zcmZ?wbhEHb6k!l$Xl7tYOG|5RZr;3k^RZ*c9zTA(V8Mcdf`WVZ?*0G&p8*?C{K>-1
zz`((v1Cj)p!N6i7aME-2(x+KmpRPH4k6ma{w6t6)bw%IW-N%f4X5Wr&{_cHgU%a-)
Votr)GNqpOrH0{qn(_mt-1^|9hF$Dks
diff --git a/forum/skins/default/media/images/favicon.gif b/forum/skins/default/media/images/favicon.gif
deleted file mode 100644
index 910c26660ca2088729309bd9286403237c68f020..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3918
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
diff --git a/forum/skins/default/media/images/feed-icon-small.png b/forum/skins/default/media/images/feed-icon-small.png
deleted file mode 100644
index b3c949d2244f2c0c81d65e74719af2a1b56d06a3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 689
zcmV;i0#5yjP)(tky!*UETcH-TCU7SrqEjJM#?B`_A)!p7(kFf9-P@=@15kkTkGK
zgFusyy#KECqZzRdBLb=P?$(kUP;>kYTDeG&{|a+iOiRbI6nbQ)j#7bOf>iF=C+|_py<&Fo1F5cC*iEM?zZGC{ejNg4LWYp=S$L6Qaby6y
zp$+F`250{%tU{Lg$5*ROH}y!1UKJS4*xqd7P(Y3JQF?lrnf?yerr%&6yGXLG1ur*B
z{$&R1@Oj)yl@%rY5rh?j(j10Yz_DBs`AKFU_QnB;)(aqQmGi&ieOS|21^NP9UMpa<
zU&p!f6RZ6Owp^X!EXA=0SbN&h?CrQK%Q3(=YBqqHD^9ZUM0Hxt-6-KT;>lf@j?Z+v
zHm(}`>85I&E<7e}oz?6UwjAogowzGO8kSN7+2`b^$Az9L{K5*ko87EV45LT-`_##3
z>d3AGh@>=mbg34|6}+-gT9N+6Dr@44VEl44O&{&|w=qpbzC#iWMKa?5)>tI+KLQK@
Xq0QFqn(9Yl00000NkvXXu0mjfZ8tBj>^LJ3~~S>i&IO88=qln{V-q
zOM40!3-puuch|@DVB6cUq=Rp^(V|(yIunMk|nMY
zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5X*aCb)TzBu@{r~^}iVZzqfg(&L
zL4Lvi8J=!8@B;EgJY5_^DsCku9AK6xWM-3k!OUU6z#qV1KKathOF(%BPgg&ebxsLQ
E0Ke)mAOHXW
diff --git a/forum/skins/default/media/images/indicator.gif b/forum/skins/default/media/images/indicator.gif
deleted file mode 100644
index 1c72ebb554be018511ae972c3f2361dff02dce02..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2545
zcma*pX;2es8VB%~zPr=ibVMCx-JQ^BhLDAsK)^**h(ZDp9YGuzZ%~j!}+w%FI;|aC7){7CdVvG)P{bng1y9Te*f}~*`1kQl$jwb
z$tlW~rRS!X?#xfm_&6tTdp_`cjgYwbRFLNdoJCN$S-yhg`ZnC-yvedRSmOh%;Y`Gl6bY$Z-}#C=#F4%9!I1b
zWQ~f+9P?;vhCxWwlwl=lrWG|7IYo;{jjmzJ5R9?f>n%-d@>kLINUc
z4wM5dAO;kq<$}Dk{2-u0$I6@2N}&cUx9nmV1dYc8jfC}%=F9WCg^OQK9C6poh#2!A
z3^EU*UFZvS^)?bu3T?J;@Ahb~%I?+@4!l5!*TjC}GIslNan-RCrrd~PdHYnNLJk+m&`$Y+NV(e>CCu%R#_8GqY4cv#j`#uRWdsg9DxWy(?oOvgCU}&@jy%c!H&-Q
zqXJxajAtmQRoRa9V-RFXXh-bK*;Fum{BjpkYQGX~i@OZ^Dx0n&H}kvGKqQ?w(6iGXu_g08T|_hp#ZvFzIwKF*a=oMJ~3UGAjZ?g}GOxm44td
zXoyYrU*I=y*vHv89hkYH(v5R#wc)BC3dZJKb3K)f>zaM3%JP(mpecViP0eKKYf3zy
z->jx_mc?mCtPEvCQ?uppk?eLJt}_IR7giW%Jr)RyI!+E-voIs*lXI*z`GQc_&D#X(
z{6G};HPYj6O|$lXxBJeDaweqa{4L=tOZCjTI^&UOxXg})LRG_cr^B9Rqt(i5ORbQX
zq`_xCRsH>xEYY%&*Nyi#{S_JZNlTm#K56`RI%7^amom;*h90Si&g1CfaFV3D|a!`3Y-GKKbL*KSbl
z>I96`TR@CqPJl(>QqB~RvK~-U)`e`l4LIqj+IU^~yyIe*|BRVB>4Bup%j{tLdKz4j
zY^<8P8m~GRGz*yv0&-RJE+-keJ+%m3wNeopzsltWd->eWmBVwUr)pX`
zK~CD<;~Z*Uy3W`3+MrEYxm5qYQ!z%YI;y7DTG`UVH0;@{M{!B&id_}3DBQ?zsotuR
zEGLdRx25nLm%-wjlnEi;-aN_1S7???rO~WgA67jjr&(vRa3y$u#kqJbeKnw
z{!T!1li9>M+sJ6AUe+*9d}2uGjhzd
z|L1Rtp8uTGYyZoQ*`DS^m2dw-X{a)l+3m?ncvn^+O>)hdd3(hMtlhkRGns{<8c0I!
zDDjpmwtj?@!6kA|iu3q+Ai;@JR+
zfk+ln&YFC{4bhK6IxVgLs4W%^8Lk`qzWU*L>yq0A3;l}{!wKZ!ue)C)SKI)9dl1hl
zhIRLV@8E}rwvE{gX(}$f6x*k)_`*Ijt1=EU-Ls6-(phomeQBgtUs
z5Xz~Cd*nE)Ac!0i4ep}Z1AugMB(&F?)#CU{Qc{Sp^vKsdL}vRB30H+Bbzrn`M##H3
z{W8dc_mDroEE+p8_}mnJtzZ4!RNe)zhB)Ds;S57nYSJxtek>^~&(7B+N5MPf2+2xx
z5Dl&4X|c@f{Kd|z1r+N|$DmsoVp*3yOdxT^J^-VAk)Z@$4^XrPrFP-Co+MXZ+KJ(W
z{JNYvraLLWA;&tRhIKOvhW|HC|L-dLvAUF(MG0(Nl?4tB{RzN7I(}Cb%hwN{crFC8
zji#aJElKvDFV+&VI1V?oUMA>*kto0^;3W8FQBSZ|{
z$v~TqE=(8DZa^i$^oht&h};P1N&wMXorKh*Z68gPV&ouy>%f36Oqkwemyeas$Qbz#
zV?7Jy%o7KY6^I=P@eCji%W`o5sf(5hySYo9$l4e2`(hIV_?=H-#R6}0$WVA|*(K@3
z=5?@RlcLh(meW%A4)hGzcvEpm(_w?>zhL*i&s9$2>r
zAtk{8Cia|+Y+V!uX9BtpXoF%lswuRKsM!pSs!?yhlCy!269K0|b
M?FSZn2B>%I-}ej|s{jB1
diff --git a/forum/skins/default/media/images/logo.gif b/forum/skins/default/media/images/logo.gif
deleted file mode 100644
index ab690de2a1c9679f225d80560cf5e06f3ed3cab0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2114
zcmb7<`#;kQ1INFc?}kmzHeo{yGuL*6N$J$gW^>CWm!Vv8cjzehrOw<6kC^+A;}XMS
z9hWqvT#jUODI~OxD7lnMhwgPeub%(l`TX$y?frVaKMZ?2s{=tJfE6$U{MhR1YFJoU
z4je8LiFS5&h(zLFDJkW6ypodA(yd#)QBgD+O_H5mT3RZVNZ!4BCvQ8_
z_=gUO#bT*c8W$IbL?S&rJmy@t@grT|{Qq
zh0K_oTq=kM3b=(hHB}l|Sa{x!2he3@YU}FjC
zB2wPg7;jqo?5x@bucWDJKv=WFQdv1#(Jmiiu3QehM=d1M%>Y4%J=mrPWvvHdlx41Y
zW{_!hs0(#@wV*PUiDa7^E4ZQ3{n41+MvK%~K&xm6t$i^2870Ve4Dr-LN8G>8!a$)k
z#3;u#TMRXp#cJ*{wMpP_nt9h$(PgvvlCQ>%z1`Gcc+knd#Ox>?ik#ISUP7
z#GoF#j`(()T+*;pki7QOd2ZU0?Y^JHaexWK{?0W{_Z}wUF4j{TvesPlwUA3#aZB+{
z7)YI}-H*93NW2`4MCuAPrZFhsV^1KID+PK(qmxx9HT5C{j_CnlC~n(<81SRAjmOZ
zuX!fW#(kJC!;9E#B$#R1gk4Z3$`g2l&BG|F95wx6RDusfYIzZrf6-9!GW!ob-
z5RpcTnp9Vz>-kOzC5~=XgNdh_RbUiRA=>DONAs;-F0M!f=HIj;uSn5mhkNdLM+7b5
zttN!evt6#TvEIC`GlR*a77ZMsw_@D)ox!xX7K_MBKlj;Xn&yVn*X$4(SGL98H(cy5
zf&^ht)zxu~uYy^YePFgdeCt_N^KETM`kCuam-B|;BBo=y&wza0g25i{{z1s9;c@Wp
ze&a9Xi9#Ln)oD&b(J~Q9+94y}IMmm!fo9$8Q7O&LW-g-snULrAq*>Yf|&p0Y!q^tJn8k
z?tt)jCuQEOXsK&=#eAh|voSsiUN2D@OH%{qg&)3si-LPzC+cf{8u&i1dX~zyBg*eu
z-bm{Ad)$|uB+vu0aLRKWOl$|hrg5OXRto?t>
zPs;`j#YI+Ta_ntCcl=0Gh^xqT($7(jnEs-ezc#2&^Ib?Ke5DdG))S
zm~`)wK)`^@k;ex?RTinBr$R8jl4C5Hx|F5vo~7Fvlqbk2V~kOpuBSdP8*NKXa5^7XcD8
z4QWsdI1r7C`5UFWdm`=0ARYoS9W+*=jQTz@E6d4j+#8f)PaQ1c(kmPH2B$~@riysB
z$$I-rbHGrePNSaPD|@pR$87%nr|^CQ@#pJ3qd^@(#WO=IiuPXGuTau6W^wK!GvRWYySSZdh?2JAdxm}
z4@L^i%LZu%cDL5Y!_|r^6v_Ot5!AE>=`9+=W)GB#c
ztC+B+x0S*yDC=ru#=O9JeRGiAsbOSn}iGDZgXt#=z(6UXyd@4U(
z9p+K1+}PylDVYqeCpV~jZunedKO8Wcsk6P?$beXTjnyac40o+ME*)eE_+<1U&jvr7
z+Dy~YW2K+-em&vy#b^9N_x0_KjWC80YkmdO^^qoUe<5Hc8J!dr=&hPb4JX4f7B{LM
z>bdE^oQoKI)chgd_$2z3TiqSyiUj&8t0wh@7R^~vNn3ykwMuQi;n{m$UugJo7o*wY
z%44O6^@w_RU$I_(C>NM#?PUynb#!n{pa@KQ2Ee)H7
io>#?{QxAKS722~IPR}q^pN0&Mw&!{uAI1ZK^?v~_GK-J^
diff --git a/forum/skins/default/media/images/logo.png b/forum/skins/default/media/images/logo.png
deleted file mode 100644
index 6a250e35b34cbe9113e3a62d17eba05d05c83888..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2081
zcmV++2;TRJP)00003b3#c}2nYz<
z;ZNWI003-IOjJbx008gr?~jjw(EZ*psMAVX6$2C~zj0000MbVXQnLvm$dbZKvHAXI5>WdJcU
zFEThUFgb%jHT(bo2Lee%K~!jg?OBPEqc{vThnq756B@|1A-n(ok1N~qO=!}S%xrb7
zs%AnXOV+av%Z4;Rgvg&Z;8pZLkpCJj0h!K4QJkm6@$+FUc8A-OQi6Xt%-^$d69qud
zrxd9=oqguv19|t$ZM&498}Rn8vU-6$Am@8()7>#@-rKCLQb-rhd811lAoD5p>156A
zaQ9oKkShLu{5?SC&%mNI{nR#%&ima)DWqNC#`giy7FByl+f(J+Bwkd-4HEuxPp6Gi
zNSIuQ-vI>Yx~sL`VdQ_vKPShM-9{;-TM&@1CIbW`XK$8LCHH9fpT7JU80bnNT~y5d
zO1ejDObukN7M4ufREgP(aGf_uIm3K-Pf>JD*4P@zxDXI3a7Hu0+wgWPQaf+h<(JZZ
zU}PH*nX~?uEn`~*l+2O4o36`JlaaE9%_4i2x67|7C)fJeQQ#g!GhCw+_UcT9a+6)uG_ly+sOBy=u@=MQ8&dWkv+`CBBPl`GVBERwcyH
z5Xd_~gxk}F0jV{)g`}F+q-I+RQ(ZlT>26s`7`lMzH+|#+c~FF(0%CWE)oruXT}M>4
znC`+{Sm=^_q2_NojwA1y?keBdp|y;|3$5@p;b*~4cZeSvNGLVgQU^5}_r)OH!Prc$
zGq;=ng4Ik08RR(W@ZnX-jx>+ByG=?etf*&OYHu`8i|G!ww(_ZiM=F0}WHMwvSAN4*
zNT!9`SYV$xs%>UnYEFIf(_LC`J;vY=#f0?8p8j)>c@g9?{nHjgR9e^F9mXLO5&DipB*
za$E!=3NWj48ecQqEF
z^h|5j;;(@KlzpWQI{Pejwko_X<5n1y_QC6hMmtm?INrLqj`j8$SL(gng>9DnVBWWP@tLX2)6p(*jrgM=V0C^$Z#*eV-zMJ0l4e`w%+CuuVA44fw
zXac%NR~MS1l?uP2j;ih{GBZ4-%8&+v`)D2l88UTG_K%^1LPqY^4E^m}D)j#XZ?0ibzk}2RDE|wMJc%MKJ>CmGMiea@KrJR%nT+qU74<
zTzQ$d!t_qwImOWPM6{DU$%H>+`6pFolsWG@fWiNDIUQpDE-x3=HGL1iB92OuF=d2M
zRq{{+HC^0xWl#9E9L^bm2@%e_4n*EtUP;e{6I$%kSOz>M+cwFhBvV(MjMRK)ew)Sy
zp$Ps_(nFWd-}Q^?07Zg_rJ4<@9h9na@Kj4mfZgZ3dt}a>y}nvq19F53@)CYZaSxMY
zJDL#Ai%P~CWa3bxJdc>a^BI&e(V!{`AL
zgpYOR)bp+a5k};sPZF_6E#CQ|f!q>g;!r8vG7t|@L>=pg
zmi0WuKtKY4u>&0}b+EK8y>fK}xMN^2NCsUm(j8SbO~%W?Q1aJD(stgQn}IYm)Az5s
z@QXr_!NN+WQCMOT7?}K21M8e15b@4Y7xTln*hlzrHUleb5mhqilq&97xv2oqBe=+&
za}|h3RByXb3?oEZ(tzpwjk|J59iA%@4GGC&{t2xU!@Uhk4vd&gerq0o&aFT~T5Mmw
zMYLww>UJIV7ZBzp8k^$oP8y>jTswDJD}baok0pFnmG00000
LNkvXXu0mjfmiX8&
diff --git a/forum/skins/default/media/images/logo1.png b/forum/skins/default/media/images/logo1.png
deleted file mode 100644
index d79a627174b08ee90776540abad2e76f28909652..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2752
zcmcguX*
z1E?4wgA2<ibF5B?oZ9u^18n1*~xFBuz3-<%yx3)#_ftUE+Qo`d)qPK67?2S(r
z0zbj_1R*mtDSopsN*FIl+1W1i27nS38t4}z)g^qLp7y;lhYt7a0jSl3L8?S)fQbgp
z9GIgd0|RA`d?7_d00faod_JKJpnwt1uSD7Z2a|-B1yhe(GKu=epgmLHL<<`k8h-xr
z#Rdb>9oZ&!^VeQMo#r!{pg-pxO=%%wiP-5se;)6v3??meND?n)RA!O@$<(3FLWQ+$
zOH6FdOV()2Oid#y4ULJnFZpl4eYEx}KeR}+M$Gk5S
zqnm}0W#xsXD4uMIn&y?8n~Sx|d}IA$=JVCuRjV$XJ}sknP^i=`V|K{Uo}IS~d2w;U
zw>#2*{<@gaLWBV!hwNSpj+t{B4+|*Cyj*>2K%9^omNa`xrFm7gG6I0)*hvDZW~2n9%+MzzWk{Tzj9=GEY?tX{kpHp1&$#)S9m54a>eAI8%Q-XV);&;*9apqP&PXYp>>%+b;E;=0xznd$s;o
zNiW9QI{j^mKNAYvs+E&f1KTijaz9V7?^FoLb3{g=Tb7$1e){vBP^z+Zns=jU$thc(
zw2kd%$grJDyMNT!)Sct9dhWuKlacvPp`-
z8Y8&LnwaoBc`{&GJVY{O#mCp`P@%=Q3Yaf}s2B6C79AD5YR#P~V>m6)bOTOOJli=W
zPNp*HEQD667cH}?Hg{}+gdgp}93YjUmp*9c`gj8_aBZ0sW8avY
zX6NiSFgF7CK79K0^heY)4c1V_P-sdc?>r6RHTL$J{dk^GMMm!9(tmz0dWka-lQA#W
zXdf`;=ExOJv+V3vx`;W^wrWq(KyLp>!+{ofPxJF9|Ku2~4KPy`&%r!MfevTFv?hRH
zP^Pv`=qS9h326iT7wKo$i=g2*B^lbLre~#DL(I(n28>2Gg^v4`z>bmBWVf432BE}!
z=%dyMYS3f}wFCnuO`6FNZ&g-=WBwi8-KIYYS~6hF|43W|sCKWJaqiix73<%aT>M#5
z?iKC8)_XL&u;F5WRi;7`VW01Uz?7QJJz;?DfTU)e2~pZhZ!ORA0JzhpA(Hd7nVDJl
ztq(E=8|M-c?lGfz@4u1lb$BMrC2Om_dxO4-tz2^b_1KuqlAiJtXpSQ6`8qPSH$8nC
zMZpy}rL*@|27`gFMY^W63My8YCbaRwy~}3>~>!x8!nLwhyfOZXVGBycPfMA^MtwF)p}uJ
zB~ORB*G&5!yTZ<$OqwMMHLF_PJ&jGd-SXLoYICY?CVF(&Vw#m#{b%s+m#mJxx&FG3
z8EpzW20O+=4wK)eCMQ3=(LXxwIZs6*uopv5ROAkrE?rPBaxA;pgO}{nULCe
zHva>TDb#W^s+&*5L4WaxR#7-B1|!KkpC9{0e>FF=@ssguLF9d|wA!E~;qk9j!Nb{V
z724{=IXkjH?b#LE~)Ia~*I(j^
z&q7!0`jgh#H!;;L(ylgBpXh9_2S(;HM+P(Zgj)SipDbK@^n^bC=|1xLztn?+(cTs;
zL9LTzC`o)-7zuSAeKT~#fv@{a-vaYz1u)j9(QBzS8#fk8^g^SuEFbTf!OCZm@e1Hz
zk~G1k6;|w?^B68VecJwAmXgBAYOPakiL>pz(w!`at=gC8Z#gHN?r?plOG7Gru6NLxkY;S=fF2z|YbO3%qJzr|R*`t+_ElUU&&^^{glpQP5mHpLzxP@1#e01HOQOj08Vo
z)cfHL0^U{i&=SYTLGdn3&Ju3SY#4p@t=L7hVjDBl94p&Pms&GXe$dl$W59QduVdfz
zK_DwVt5H0(psqg{TB3EPB_MT+v
zLYBDN^U+I)`GZ+Th^mq2lakVw_C)=`4cD`4R&i(Ib*)VL&w?yA@5bAw@19`~{B3xY
kse_>K@2>xA`@bf7AU)z?-rE%2I=lr1p}dH|I)5bN->?*N_5c6?
diff --git a/forum/skins/default/media/images/logo2.png b/forum/skins/default/media/images/logo2.png
deleted file mode 100644
index bd3cccd9f47793f86864cd068621ab07198b5ff0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2124
zcmcIm`&Uy}7Cw1j9(f?ogb*T6
zR7xF)f`B{(Do|8Luizj$s9*~MRt2>X1!+Z~1*!Hr|H7>K;heqp+UuPCt?ygs+owPh
z9z-=}83O>QA;AHWSUia>UkH!w{6lRrED-njZSe!3sdpXe0oyJ}doah{1$l3>n$0jEa145Xm{l=`0
z1LOGW4g>)b$0!*Rr7zHbv%EOo9Z|$Fo71*7zJWCy1avV4C^u25yk)w+l|hG~+NP%M
z5{YQYU@)jZ|6}ppWJ?dj(b2ICDyt7~YcEeqbnFp!&dzSWetl`j4p9%R#_ovkDqA);
zH&^*-g-KjDYY(ZuwsyF`U!+y!<>j?5(BX=hvuhN^Uk??}&Q{n9Y4Zjg#C7RoPy$Wi
z{*X1KD~4_Y4Z$7tC2Omw9FAC!5Acv66G_Q!o6uY7#pImab!WWHb~w?Z0Yl``%a$|q
z+>?+r?Ts74s#CZ+-0m|}TfQ#NUh)?>(Z$J0GLxDv2Bn)2Z1H?F;I?WNsG~%EmS7Pjk|D)ZWt7A)huHmXOer^gBJH%1K9jp$`#$4x
zb~w%Q?@pblQ{_46k=OZ}+xH(tNCYC#!ac#N_JOZWKf7U{DDkgy2`d$SnL*NS*Lyk0
z%meWH?Wzzz0i!T%m4v~F`SGnry4R#TIUJ1vJ3A0IipV;YRq&)YZGt}EW(eud{
zE;wK6z~y^1FP?Bu(p|Q0-uZ9VP{;H9U`&*-j-@vuhP{16BhsxE$`{EVKiAp{q~8uq
zr@u2SsG*&O$?=ivqQiihvx^*$fSkYwVxu7!GvA=hX{$Q^|2i*Q5IBWivcFZ#_XbQb<4Md2}~BtK?7vNsOe@;
z8A7rc8k5Y~=#YTZ36{>eteYjoWt1`AYnuXw~+sOqb
zwxMU@^F4f%7!EBh=q1a|wAiZ_D+JOhUX!JJa2*$iS7#3pi1YLFdk4gKV7j*>o*xr)
z>u|a52rtb9z`
zL5qYJFB}IWKha&}+a4CAY57e4)62=pAG~t$p>#RjdtqsQ+_oJzW`~!f=cC-qY`D
zk$bnEefjm5qa|4Mzj(KDJvR>pqy+7to)KGbH~EW!4A`pNaZ
zoo-AVwbdl!!x09)Z|CpRy=ESskd{ID`jXDLfL?hW9X-=U$GpHyAgw3%zFZB{#O
zNiG-P)g6G3eOSK97L?xGE4MfIYy#f`>zXaygC;%Ei~H&H6ZB;yzeOe+$gdv&7;t@C
zrZ*)T@!lqtLa&6cr>4Y#2wypjY&gET2c|!nXb&Nw67mG0^=3cjK+>=HMrGJ-oxxOB?TeD9oE*q;kn@I-k&}bV{vSuh^`1%k6r<;IMd1E+ZG{w0g~MyWjA*d`_?1
z@A$la&+q&HfPsR8goTEOh-(y!jE#0PICFAzz_X{%
zpFo2O9ZIyQ(W6L{DqYI7sne%Wqe>k@wW`&tShH%~%C)Q4uV7~+8cVjU*|TU7DOk(4
zt=qS7(BTJr4xw7TUm@^~T
z%(=7Y&!9t#9!C>oFt6t5zwd>cgV;`7JySDAyxO3~?&AYen-@tu`Sa-0t6$H)z5Dm@NG7S|
zk}d||0+o_zM{=bwNED(Iku7HVjkApih7e}UBq
diff --git a/forum/skins/default/media/images/medala_on.gif b/forum/skins/default/media/images/medala_on.gif
deleted file mode 100644
index a18f9e8562941254941a446efad3e6edcb651d9c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 957
zcmV;u148^qNk%w1VJrb40K@E)v5}!;
z41e42_PDkdcy;l$Dm3n3(BTJqC>oFt6n{%wd>cgW6KueptkMXxO3~?&AYen-@t5f($n3;DZoGDB*+&RA}La7-p#9h8%Y2;fElG
zDB_4DmT2OMD5j|5iU_pm;)^iGDC3MY)@b96IOeG1jy(400+o_zM{=bwNED(Iku7Ha6Bh$gD&q69SR=%bKED(R$@R%+>`m}aW!rkr-_
z>8GHED(a{Mlxpg!sHUpws;su^>Z`EED(kGY)@tjmxaO+st^xGw>#x8DE9|hu7HjOW
z$R?}ovdlK??6c5DEA6xbRBP=zwb*8>?Y7)@>+QGThAZy4+ZYo
f#w+i<^ww+dz4+#<@4o!@>+in+2Q2WwApih7u&Gpl
diff --git a/forum/skins/default/media/images/new.gif b/forum/skins/default/media/images/new.gif
deleted file mode 100644
index 8a220b531225397b6a304918e4d96f6196ef40a8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 635
zcmZ?wbhEHblwuHIc*el+@5|9!VKPrU6JO0wzv{&J_sfTWzkfci3%c&b^ZDfVC#}&>
zyOaNZ`}}-T?z6u1zh6H-?MZpollt+%s=r@9J?->=+L`dQ-S^kCd)M4KzTZCopv3i7
zg#7c#dEf6|d^NB3$Neksx6gmOcH+A&vz~S(UH9g@mudETVb^R-1c%-`NuBSmGx$*^t}v|N3{XpZ=QLxvghB=@852m{z+->`t|DZla{DoFCJX;;C#~TaW_r#|NsA29T^4|DE?#tJ3t3Sg5rdM{YC?W
zNN`JQTYE>a2m^CyU;l)Olls+yCQqLcG;PM53A6e*r27K<7?(+e+V$BkQ0bc=>K)=6
z>S?AMvXf0cM9U_`CsbZ({cMkrfKVO*0bx^n7M2ip!4S?%Tu1x7I766NnV3TOd0ax>
zT^02BokLcHZqTpCs*6-yL$WVW?v
z$*r&`a5&o0$QRlWP#C1jBFZZwJ5}J(3P%A63#oz=VNXx?YB3g7+`6(-c+I>yOH7zf
SWil{ucTQ5<#GoL+U=08@?DshU
diff --git a/forum/skins/default/media/images/nophoto.png b/forum/skins/default/media/images/nophoto.png
deleted file mode 100644
index 2daf0ffd4333c90aafd71479510144bcdcb16c79..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 696
zcmV;p0!RIcP)FMR=<>BGs+}zyR+1b_A)zQ(>&CSip$;tWo`Nqb^+obycyPRT{LUB1#`*2@<8tPF=iD3b$&izilao_AJ$Uu|
z?T7EI;9G2Ni9S*S|C71nPeS~@YXtLGZ#Xme_@@>8G%0p2sX6212og-Y1w$?e
z!WtwvbPJl0Aa)A^G6(_^hxshQF(4>p2|9paHcPMx2&P29rJ}2$!H>
zp6f%wU$+bD9?CDf;J*mCI1D_PzD=;|;mna;B%E*;B%w%FK=_dsy!3SVseTRsf;O0PA5l@7S3xZ$It;!i#Ky
zUwupv47dxD@P=FMLIa$I+=l%s0_x#ZiV;DS09Qn^j9pByOm?A=ICBWn)?Lr=Eg7SN
z03L<9;aJ866<{Oli{Qt&ARUsBlRXUURcV2;m^$aahm^dfY4!Hqk3O{)q1D
z1TF3(2qBkcAppN3{)S!D3NGw#J)8~}C44zdYXvndi+CRUT0sljYWS7$P%CI+|Ayli
em8wo4&)6FRqy5_K=O4cS0000${>eZ`v@813YY~Q2faPbmo5s9gt%|d4ho>o57Mp#$&^RgUuZ5eg+dZJYZnp<>ZnO
zn8@79#>ir^py2~!6AQbMoJWI#lOrpaM#=_*i%myaxx_eX7A$CT;APg}>iKX$rJbGI
pjAPFPVa3)-5*81xR0c3Hu?omAG~5VaYGz`Q2)lD?YqkS}H2_zIYl#2=
diff --git a/forum/skins/default/media/images/openid/aol.gif b/forum/skins/default/media/images/openid/aol.gif
deleted file mode 100644
index decc4f12362124c74e1e0b78ad4e5d132621ab23..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2205
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
diff --git a/forum/skins/default/media/images/openid/google.gif b/forum/skins/default/media/images/openid/google.gif
deleted file mode 100644
index 1b6cd07bd8b9f27175e0a03be3b5849e0f5479c7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1596
zcmc(e`#aNn0DwQcU}GKMxn#>^B<6OWQKZt-cb3xBiE^HYQf=ZKSu)*APi@FdDnu7l
zbE$~JN>R#|+lE{hiz(BRl~d+uOl9Xe=bt$55AUzjP!`6aB*XlLd;L`$MA0-=oh;_@%=DSKzTW5!kkg02oxN<-;QgzZXZ6@(M)5
zb-u1@<^vWqN`U%R3CtOJuojJt|Ec%yo>_yZ%MV%ajx`i>ysy4d0^mKeNIQ#t@2y*KMj}jE8i*m
zl#eQZ7!1Pfn72wr)$QwY6gabm(O6$Lylw3Pd(s%KbU5crNh3{eeZxI4(dJKB1MUAN>n;QZCCkQs_tGtJY-^iC3ja3x9v_uTP?vFVW;}s>#
zx;OS=;!{FWer7!3y~sj6t|9~xWcTkC-QJ+z*`
z{$kU`Oy-`7hp(oZDFCEubef~t-M)K#*_S^K7T>g7T^A0(oK7oIN_s0RehD8}s1*i-
z0sK!m=+Xa`J^uB-PXLSoRAEU$)j~-M#?mcPT3yt{hn6u4gKCOf&J$L|DbuOJmz+s9
z2lJ2J?sjAok}A_-F35;rl&1P$i@IJDD5Bmxks6yO#s#L4K20(2&;kgjBXbWq$Y}&>
zCc!g3Y<~;U$CQlDX2nU|3Dx218gg&b%2GehXilrp4#P1y;weqVUjQptQlB1c;VO7e
z@*D^wqyiQ&MP@@Eiijj!C6&AUPa6q7&lm^MToqj2ny^P9>WX^Ttd-e`;&k}6HltTz=X|A>@zzv6@
z)2r$ZZK19Hq1s>pg(Wg*yF5bqcpgSu2*Z=3Y48rhsMseSGm6D#RWnKSz*rI2&e%)U
zhJ)uMj-*j*Cq6a|SR^kuis=outMPl7vPf8@-OF`fK(;t_tEV6%zOcxe#-=20Mq7|L
zi=0@wEQshFjSRBhL&)v46Z=EWNGr^Y7(cjs9|q}uirjP>UklMq_Cn}BpIZ=OxlIZN
zrqgmZ9Daq-5mP(tkggoG%l1Mp5`!YX9P+{9a@vc{UA@ux9w!5<$J7Qsxz)kjp9U>O
zdMGwqZ?ce!P4?kRO}wMe2<(jAw{y`J9zkIp$bd;9jpjy5e7h8h$r8hU6K*}g{&bHR
zc4&^sGRyCwh1g@leXv&EK^8KfI43fLjtGZ4%_yBJq-Q6Ii74-1r$U?ItVA?Z^o7I@
z6Gx_VGVMkkya7VbaJCw^O!RGWg$&GOOC(&Bu>?}_7>IP{%ivHGBI}%WA6YI2+3M9H
z%iJLj*4d~I--NgTJ(9t@qodNU?AaV`YHlKR%UVir5C+OujBse~Mp}pFlaUEl5;MVK
zxnv+FK^_1to@fbJn^<{1g0s+?k
E17=sO82|tP
diff --git a/forum/skins/default/media/images/openid/livejournal.ico b/forum/skins/default/media/images/openid/livejournal.ico
deleted file mode 100644
index f3d21ec5e8f629b77c77615982cef929802fbde4..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 5222
zcmdT{3s6+o89vBPQIq*$1QbbvAP7Q0Tpmk!l@)pHf>>S>
z1vbL6JXf&Eq~nt$W+Y}?NhYbg%X2}TX`y3UlPS(H)68fPIsMMP%d)ZvOQtiO{(Js=
z?m7Sef9JdZJ&(JD*bxy~wQ3b%;$cU~E2vFPBC#hV6@5-lg8iE%gba#Un|TxR-jjq}
zb3h#KnTHdU;W5$jSK%T=Pj@H?K_Lo-P~nPOqSb0qGXv!dp_JW0@nc==!i$LGD~{u9
zt~8U?B2Fd~qvpC~M^KA`gqqWJF*i|=E@%ujnuad%Of!pb5`P(QC7kR#SP2OTzQ<#W
zlv%8aIKD~feX!6nCX`OuQ922|4;{CsbQG#}pj5FEW=E(mRL;B7LWEq0KANYW^3Y=B
z_y|vmH;?ldeDaRw*8J(~hC!qOw?4H%Oa?Y1JnqbNZuG{_4-W8zh5g(k>k33gQDw%FD{ne3XB@
z!rC7_g|Gdhw<&R;HMK*r`+|J;KQGI_{V?zIn&?P3mLE&uo!9m>GUrS3pvy(U5A^kP
z_$(c0u8#D!b_R{U=43cu$bYTlSjYsNjhYF)s`QQZ*3Ky}xtxmjy4jX?u^{kLVeo`r
zO$S;h4E5!2dRRN72QywCwpkgq;m=hECwm&HvGsd>T}g=BENAdX&yl^G;15ZD@oDXe
za5~)ny+KtH;%e1SDcr|-+q7@ZKZGT0tS19Bm$;2T93LRj8^{4y675bB
zwk%H%+bJ)|KhfTK`uexou|aVjGo#E;Z%!+~BizWLXGzMgwI!$9Yp#x{^ivH+c7UV2
ztG$yM@p8QPk{Hi9qZjA|_iy0IrBz^V2FDGCt7!c=X2YVB&%j+s%GRZq6}$VIQ&2P#@0_gE
zNAUFwKQH_3h_#F3ZUXmN_F>UpAnQ~Ky-mq~NZp*1ER$dEt(TrWr;qctLx_#Sm^+j7oj?A#I7DC$
zaD!T6s6Sj3{L6HyM1HcisHVO0oT2v1d(D5PMI|R99BoZLz$4vBrg+7t)3*@)h!oupL+wwg_YH%9vj*4aIjHXba4{8xkuc);A<=
z{dZS2HK=KK!_1Gt{0T^;r%|h@D@A
zwEWI*^|gxqvzS+eyR}HSKjd>V18&q9+tQP_E(>?D2|U7;$hbAy$_UJGbI$ekhw(yN
zQ(t@t+Lp`(@GUljJClQ+q->vynK|C(jk-g{A&xDnJEp9_2N%N}naK~`m>>4s;pgp<
z9QJ=jEdleAH`nptjkB2FdOtgL+Y^|;w&WZ>d7<84e)0JS>Jtat2~yqPk^=D3P?xq0
z@f7|6Jern%&D$w>p*GAGyl#LeY+CZk`Lq2)w{GZkg^@2rdyw`F-&0YZ?HMmk^)y+7
zCvI74%WRnjCpgYpE3*5H!##ZipIx~q^<9GpuZi7A2hXs1Zk~!pO}{2J>4l}(OBYOw
zFj0B(Bt6hCeb>`pT-SE@^z{x5U2Ln3-?S|8w?F!!Jf|8`_0G@Mi{w$
z=X|SDy7MOoto~1%!)>|A@LPpZ)J;^~NDC9o;+|ify5q^Z+~}r~bWFtv8#b;5N6F%T
zh9Q2R{a&r|U)2@;IgyF6UTmI3tzX1!nST@^QP$V_qZVtm#9wTzwZCbi{LlWU{0Anp
BEK2|Y
diff --git a/forum/skins/default/media/images/openid/myopenid.ico b/forum/skins/default/media/images/openid/myopenid.ico
deleted file mode 100644
index ceb06e6a3f0d88fb97cf10475a3062fb0edab33e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2862
zcmeHJJxc>Y5S@6ZrZbS)owhjA&;jnXfl*X745tmMB3MrBW$)n`RP3b417qd6J6bL?UM{+)q(3xlv0Ik%T7j-l59$8$m>#ard~jq(8yIciUu?j(!>?&WKIe?G5c$>`
zV+r!H1WS-ha)=*H^!uZEl>O1BP8%xc=fconk~~4DvKCYWn4`b_pUnoZ788}%mc>uh
z9HV;s)r}P~NWtqf_p5&HGjTJoZI|S8n)uf0l05g}rdhqaIBpnv^myAWbI*7E=&N3x
z95xI+0zR;x-|08c&;6Ul#XdjZARV+no-wSN`};1(+>)AIisPc*P@H*_1Kd%ys#()H
z>NUmL)tL6ccj9UxPF`{LG^Rc9JypwV>?^N0yvK~LBc9f{#^OA9dQUv#8Tz7oxfa(K
u#=%<%_2}PpAb{bmUKcqz}))c5uC(7v?)v4a2P)ZNa-
z@$&T2)z|&~{r~^}A^8LV00000EC2ui01yBW000GQ;3tk`X`bk)Wk@<6#nZYULKH{p
zEx|?+kif!I0vIL|#ZMubBmjWH2OtmxIFVa~6JQ7!1CK!f5W#StOTv&C3=E8h2vI1s
n+#cd5;2fT3B_0kF0v!+!GARoV78n&7dMN`JIW(4+BOw4gP{MS*
diff --git a/forum/skins/default/media/images/openid/openid.gif b/forum/skins/default/media/images/openid/openid.gif
deleted file mode 100644
index c718b0e6f37012db6c9c10d9d21c4dea0d0c01bc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 740
zcmZ?wbhEHb^k$G`xXQrrKS$vIw-5i{JotZf!T-qt{~ulWe{RG7ST3!;kh({9A)7tlU&$%5*g$f^Ar>#
z6BXsynVh)
zJ?osJf(s)D10z?npvQi179P(djPk7Va&pI%NfVlv<{V@&c*Q1S?C^%corz71;ll+1P9`Sd{~>qE3OQZcJ(5}z4)|O#`p@vJ
zr9;Fsp~HjOs*vv^`}=;ISs7Cf1bMkUpV%Pi{6*$qgF{F5!;-XxTBi=%eG5gPtV^!wdin~r1kkVO&vN=8Ce+Xv
z$*|MCqlY(ACp$%vPrNeV;U*4lo)c<`?yngdvl5)QWEV6rHbi?!)@MOEE;CkPT#L!!@77y7u
K{UjJ!7_0#i2pVPp
diff --git a/forum/skins/default/media/images/openid/technorati.ico b/forum/skins/default/media/images/openid/technorati.ico
deleted file mode 100644
index fa1083c116527de7cdbf5897976aae8807fce878..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2294
zcmeH}ze^lZ5XZl}d+sirdNzWcsg4x+2g0RLIAWPjuoD!tN}VeB7eq*vB1MXHX)Xi{
zAytZyMi86WScxK7SO|(gE|<@|cRuzut2sy&xMBC*%tj+A
zE!c_l2H#`zaX;dYlru_mk^31KdcB@L9&W3#wIFp`YJOeP`OSr1{CK6O-`2Es@?E#T
zy1MFK>-Ep~I=Vd7%e_s#J@}-Zvwh`X=4ClX_h=7BXW;)k1Ifb@+v7M5AZI7aYkiNm
z$KjX;Qm=X2(~a>=Zn(6-IG8mv>sbkaBEroVrCFBdHr=3XM61uF&u!`5GI1u;Qf<+zMxU!sr1q{f@m!ic#&7x4
zjcDZqv{&NU85cGO2UiL7I^;$4kVpqJ@-E;pjlK(>l3v2Y;e5IS@q=)?(5+CX;*7LwCwnlxopJBahPURBFV+~&J
eFF>coTp;}_7zqbJAQuX63TM^f{)_Klzq8-HK65Pq
diff --git a/forum/skins/default/media/images/openid/twitter.png b/forum/skins/default/media/images/openid/twitter.png
deleted file mode 100755
index 9a6552d18444ac98fdc776e6c58b794dd4452772..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3130
zcmV-A48`+_P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf3(rYJK~!i%?V5XR
z*3}uupOzLVw_czvr6Mg(q+BfJBAtnwnhhP85N5_MMqy6CWenXVS+k6ocCk@F`KpDYy&d*
zrm^YBv+>x~<$2W6{i0#u<@P%U?%dX8R`^eZPDlH^|N1tw`C|q~s7fsGo@%qZGR)p#
z@6K|w%`IjReqdHM*la)X?#L{&k!gwYR5Y8-1N=Ed%no#$)yy=@X3apiz!Fd45|{Nx
zA2A4F&JUazd(M~@p}h0#ExjFP?>(Jn6BxUgVm6GiV{!8MRJiwCbnQiGMnQWFCESP>
zfS5wuK?YO!QAYG_Akua&mJ?yq;cVK;hPi-p8mr>Jpz*VpOkt4D3q8q=@QSB5s?JC#
z3;(`GDwa9wRQ(i08kP+Qk-CCppyYyudI^uT4cS_
zS$csec`_L}fQwZUmo}g3>2EffRWR0{b}c1aHlY1us@abV&0e1p<*tkzR-iv<{DC{-
z=)$t~2OJl{`g5eK(j=TW3oHC1!Txgx%pM_A`@W>sY%xLn_kgJ;SE@d3b~a=+ey46u
z@Ndm|pA3o6#hl_`yPK2ZW@egwdm=H>C54MZz?q;PX%5kNVY`|1V_`aNcQr+}5<8C*
zXVvxISBsD{8gUo(2G_%D684h(qZpk)HY^JDV&2EnpFSF0{-Yyga7vKXhZ0424w?mf
z0j4f-?qy05&3(ts2_MD4cOxO%kQol*{epAZ3kcU4GO=(B)Do{x(Meg!+HV@omSG$V
z$o})`dVh^EDLz@-iubLcj1sNZJV3u6W7>q5`WBQpwOGdOlo?ZTEg6VUGQF5`kTk$;
zgJ5TwS@BM@9IUnl$+wpRI3SmTM?aZXOz)lVwiXWky9}CwYHwy4j==
zW{;wbP3>mS;?iHa$?R(gy#?X3P?`Xl4NWHRa&z@b?6vA(B{o#nH7W?q??`mv~n)3J%ITg@uPkn>}}HZx{vOLx4O)N_6I?B~p+KYCC6BOKSBB
z);?aup2~nMP6eDUfa>ITwn`$H)G9N$)&y|ppvz(|oi6kDt%1@d4c=L-%~VN(o5<~7
zMXN>dU4?b4r75vFYZQ;I(EOGI{edv^M#St1==L&}h0?tN4RLg@JGwt{+gU~N{#Skr
zK*jT?@B(?)82HNoh$1$I-{a{UPMWWG-B+lE`~pW70wbpd$uB20}Gm<
zY2sW81GhoDmfKOMiwVyHsCZ@|OAw;@p&PN{u#98~e1^KiC4hJaqK|W$r^e$h<9hlx
zd4F+&*>axez53$-?6J9?x4><}&7tGH$xjDh$l7_xbz>;vUGD;1R*(q@mqPsG
z=`^K7YhfKpMDg_u?`6181-G)DCAd3AAy8zk0_x8wN9{G9`S90rg{WrwIl2;QJM@p`
zMdelN1)|2<6oUZq%Ye0<80Gs4-j~wVk|gA0`1Ls!wlpY_Eg-2I`$=p-BK6Fo-|Z=@Oy4ujCn
zFr&8+M=0gVYCZ*kpT!b}GT!axL?=>p(dqE_VsQ^+X>XIX6bCh)3-6!cw)3h0c(>US
zGOY(89bShx{60d=aOY+1fVmnE{8GNz!d&*HFQWSkNslGQohH>G!8W;i=DWsx+Dv=)3})@bv&G5H&ss(C6hi?RdfCp27L00Uzdu@qQS0@rM>K
z-~C#5D-3?havXW^JE+l|Fo7$EkdnLkeKaf=iw9CP6G#C5HX_~vbf~f_<9V)J(p~~v
zpaj;(KeDt8&=OrPbpC61IK)aOS*c=w2f}Edqkt(l*N2#L++5b~BZ@xE1Ry1|AO}b=
zub7}zE16`3;@paZQGT)vWxHK7ssE@Ee{k4r9Dp3=q!rUC$cbg2
z*cCmDyhI-)R56tEfkM_Zh398DyxQFNqsc^cD#OZIdna8qD;$WHP)5Gp7TNM&L?f
z+XjOC8fr2R;#roiw_>4nA3J)>>i6R^lE(|@Q2X8S;)Q=zHro}MXO?7|SF%mO%fzioEX>2NsQrkeR
zJB!e@2