From 950c36db5bd0540ffcc60a8d89cae303db12b941 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Sat, 8 May 2010 23:19:05 -0400 Subject: broken commit. in the middle of moving conf to models --- django_authopenid/views.py | 9 +- forum/admin.py | 3 +- forum/conf/__init__.py | 57 ---- forum/conf/minimum_reputation.py | 148 ---------- forum/conf/reputation_changes.py | 149 ---------- forum/conf/settings_wrapper.py | 59 ---- forum/conf/vote_rules.py | 69 ----- forum/const/__init__.py | 137 ++++++++++ forum/const/message_keys.py | 19 ++ forum/context.py | 26 +- forum/doc/HOW_TO_DEBUG | 39 +++ forum/doc/INSTALL | 288 ++++++++++++++++++++ forum/doc/INSTALL.pip | 31 +++ forum/doc/INSTALL.webfaction | 345 ++++++++++++++++++++++++ forum/doc/ROADMAP.rst | 88 ++++++ forum/doc/TODO.rst | 70 +++++ forum/doc/UPGRADE | 24 ++ forum/doc/WISH_LIST | 55 ++++ forum/doc/askbot-requirements.txt | 9 + forum/doc/scratch | 12 + forum/documentation/HOW_TO_DEBUG | 39 --- forum/documentation/INSTALL | 288 -------------------- forum/documentation/INSTALL.pip | 31 --- forum/documentation/INSTALL.webfaction | 345 ------------------------ forum/documentation/ROADMAP.rst | 88 ------ forum/documentation/TODO.rst | 70 ----- forum/documentation/UPGRADE | 24 -- forum/documentation/WISH_LIST | 55 ---- forum/documentation/askbot-requirements.txt | 9 - forum/documentation/scratch | 12 - forum/feed.py | 8 +- forum/forms.py | 9 +- forum/management/commands/send_email_alerts.py | 21 +- forum/middleware/anon_user.py | 4 +- forum/models/__init__.py | 2 +- forum/models/conf/README | 5 + forum/models/conf/__init__.py | 62 +++++ forum/models/conf/email.py | 20 ++ forum/models/conf/external_keys.py | 88 ++++++ forum/models/conf/flatpages.py | 38 +++ forum/models/conf/forum_data_rules.py | 53 ++++ forum/models/conf/minimum_reputation.py | 148 ++++++++++ forum/models/conf/reputation_changes.py | 149 ++++++++++ forum/models/conf/settings_wrapper.py | 59 ++++ forum/models/conf/site_settings.py | 81 ++++++ forum/models/conf/vote_rules.py | 69 +++++ forum/models/question.py | 13 +- forum/search/state_manager.py | 3 +- forum/settings.py | 1 + forum/skins/default/templates/about.html | 20 +- forum/skins/default/templates/base.html | 4 + forum/skins/default/templates/base_content.html | 4 + forum/skins/default/templates/footer.html | 2 +- forum/skins/default/templates/header.html | 75 +++--- forum/skins/default/templates/privacy.html | 27 +- forum/skins/default/templates/question.html | 6 +- forum/templatetags/extra_tags.py | 7 +- settings.py | 3 - 58 files changed, 1995 insertions(+), 1584 deletions(-) delete mode 100644 forum/conf/__init__.py delete mode 100644 forum/conf/minimum_reputation.py delete mode 100644 forum/conf/reputation_changes.py delete mode 100644 forum/conf/settings_wrapper.py delete mode 100644 forum/conf/vote_rules.py create mode 100644 forum/const/__init__.py create mode 100644 forum/const/message_keys.py create mode 100644 forum/doc/HOW_TO_DEBUG create mode 100644 forum/doc/INSTALL create mode 100644 forum/doc/INSTALL.pip create mode 100644 forum/doc/INSTALL.webfaction create mode 100644 forum/doc/ROADMAP.rst create mode 100644 forum/doc/TODO.rst create mode 100644 forum/doc/UPGRADE create mode 100644 forum/doc/WISH_LIST create mode 100644 forum/doc/askbot-requirements.txt create mode 100644 forum/doc/scratch delete mode 100644 forum/documentation/HOW_TO_DEBUG delete mode 100644 forum/documentation/INSTALL delete mode 100644 forum/documentation/INSTALL.pip delete mode 100644 forum/documentation/INSTALL.webfaction delete mode 100644 forum/documentation/ROADMAP.rst delete mode 100644 forum/documentation/TODO.rst delete mode 100644 forum/documentation/UPGRADE delete mode 100644 forum/documentation/WISH_LIST delete mode 100644 forum/documentation/askbot-requirements.txt delete mode 100644 forum/documentation/scratch create mode 100644 forum/models/conf/README create mode 100644 forum/models/conf/__init__.py create mode 100644 forum/models/conf/email.py create mode 100644 forum/models/conf/external_keys.py create mode 100644 forum/models/conf/flatpages.py create mode 100644 forum/models/conf/forum_data_rules.py create mode 100644 forum/models/conf/minimum_reputation.py create mode 100644 forum/models/conf/reputation_changes.py create mode 100644 forum/models/conf/settings_wrapper.py create mode 100644 forum/models/conf/site_settings.py create mode 100644 forum/models/conf/vote_rules.py diff --git a/django_authopenid/views.py b/django_authopenid/views.py index 4f7d3efa..c12017b2 100644 --- a/django_authopenid/views.py +++ b/django_authopenid/views.py @@ -35,6 +35,7 @@ from django.http import HttpResponseRedirect, get_host, Http404, \ from django.shortcuts import render_to_response from django.template import RequestContext, loader, Context from django.conf import settings +from forum.conf import settings as forum_settings from django.contrib.auth.models import User from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate @@ -616,7 +617,7 @@ def signup(request): 'authopenid/confirm_email.txt' ) message_context = Context({ - 'signup_url': settings.APP_URL + reverse('user_signin'), + 'signup_url': forum_settings.APP_URL + reverse('user_signin'), 'username': username, 'password': password, }) @@ -760,7 +761,7 @@ def _send_email_key(user): message_template = loader.get_template('authopenid/email_validation.txt') import settings message_context = Context({ - 'validation_link': settings.APP_URL + reverse('user_verifyemail', kwargs={'id':user.id,'key':user.email_key}) + 'validation_link': forum_settings.APP_URL + reverse('user_verifyemail', kwargs={'id':user.id,'key':user.email_key}) }) message = message_template.render(message_context) send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email]) @@ -1118,10 +1119,10 @@ def sendpw(request): subject = _("Request for new password") message_template = loader.get_template( 'authopenid/sendpw_email.txt') - key_link = settings.APP_URL + reverse('user_confirmchangepw') + '?key=' + confirm_key + key_link = forum_settings.APP_URL + reverse('user_confirmchangepw') + '?key=' + confirm_key logging.debug('emailing new password for %s' % form.user_cache.username) message_context = Context({ - 'site_url': settings.APP_URL + reverse('index'), + 'site_url': forum_settings.APP_URL + reverse('index'), 'key_link': key_link, 'username': form.user_cache.username, 'password': new_pw, diff --git a/forum/admin.py b/forum/admin.py index 3afa2241..41b68b9a 100644 --- a/forum/admin.py +++ b/forum/admin.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - from django.contrib import admin -from models import * +from forum.models import * class AnonymousQuestionAdmin(admin.ModelAdmin): """AnonymousQuestion admin class""" diff --git a/forum/conf/__init__.py b/forum/conf/__init__.py deleted file mode 100644 index 6625ca5e..00000000 --- a/forum/conf/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -from livesettings import ConfigurationGroup, IntegerValue, config_register, SortedDotDict - -INSTALLED_APPS = ['forum'] - -MIDDLEWARE_CLASSES = [ - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'forum.middleware.anon_user.ConnectToSessionMessagesMiddleware', - 'forum.middleware.pagesize.QuestionsPageSizeMiddleware', - 'forum.middleware.cancel.CancelActionMiddleware', - 'django.middleware.transaction.TransactionMiddleware', -] - -TEMPLATE_LOADERS = [ - 'django.template.loaders.filesystem.load_template_source', - 'django.template.loaders.app_directories.load_template_source', - 'forum.modules.module_templates_loader', - 'forum.skins.load_template_source', -] - -TEMPLATE_CONTEXT_PROCESSORS = [ - 'django.core.context_processors.request', - 'forum.context.application_settings', - 'forum.user_messages.context_processors.user_messages', - 'django.core.context_processors.auth', -] - -TEMPLATE_DIRS = [ - os.path.join(os.path.dirname(__file__),'skins').replace('\\','/'), -] - -def setup_django_settings(settings): - - if (hasattr(settings, 'DEBUG') and getattr(settings, 'DEBUG')): - try: - import debug_toolbar - INSTALLED_APPS.append('debug_toolbar') - MIDDLEWARE_CLASSES.append('debug_toolbar.middleware.DebugToolbarMiddleware') - except: - pass - - - settings.INSTALLED_APPS = set(settings.INSTALLED_APPS) | set(INSTALLED_APPS) - settings.MIDDLEWARE_CLASSES = set(settings.MIDDLEWARE_CLASSES) | set(MIDDLEWARE_CLASSES) - settings.TEMPLATE_LOADERS = set(settings.TEMPLATE_LOADERS) | set(TEMPLATE_LOADERS) - settings.TEMPLATE_CONTEXT_PROCESSORS = set(settings.TEMPLATE_CONTEXT_PROCESSORS) | set(TEMPLATE_CONTEXT_PROCESSORS) - settings.TEMPLATE_DIRS = set(settings.TEMPLATE_DIRS) | set(TEMPLATE_DIRS) - -#import these to compile code and install values -import forum.conf.minimum_reputation -import forum.conf.vote_rules -import forum.conf.reputation_changes - -#import main settings object -from forum.conf.settings_wrapper import settings diff --git a/forum/conf/minimum_reputation.py b/forum/conf/minimum_reputation.py deleted file mode 100644 index a83d94fd..00000000 --- a/forum/conf/minimum_reputation.py +++ /dev/null @@ -1,148 +0,0 @@ -""" -Settings for minimum reputation required for -a variety of actions on the askbot forum -""" -from forum.conf.settings_wrapper import settings -from livesettings import ConfigurationGroup, IntegerValue -from django.utils.translation import ugettext as _ - -MIN_REP = ConfigurationGroup( - 'MIN_REP', - _('Minimum reputation required to perform actions'), - ordering=0 - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_VOTE_UP', - default=15, - description=_('Upvote') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_VOTE_DOWN', - default=100, - description=_('Downvote') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_FLAG_OFFENSIVE', - default=15, - description=_('Flag offensive') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_LEAVE_COMMENTS', - default=50, - description=_('Leave comments') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_DELETE_OTHERS_COMMENTS', - default=2000, - description=_('Delete comments posted by others') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_UPLOAD_FILES', - default=60, - description=_('Upload files') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_CLOSE_OWN_QUESTIONS', - default=250, - description=_('Close own questions'), - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_RETAG_OTHERS_QUESTIONS', - default=500, - description=_('Retag questions posted by other people') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_REOPEN_OWN_QUESTIONS', - default=500, - description=_('Reopen own questions') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_EDIT_WIKI', - default=750, - description=_('Edit community wiki posts') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_EDIT_OTHERS_POSTS', - default=2000, - description=_('Edit posts authored by other people') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_VIEW_OFFENSIVE_FLAGS', - default=2000, - description=_('View offensive flags') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_DISABLE_URL_NOFOLLOW', - default=2000, - description=_('Disable nofollow directive on links') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_CLOSE_OTHERS_QUESTIONS', - default=2000, - description=_('Close questions asked by others') - ) - ) - -settings.register( - IntegerValue( - MIN_REP, - 'MIN_REP_TO_LOCK_POSTS', - default=4000, - description=_('Lock posts') - ) - ) diff --git a/forum/conf/reputation_changes.py b/forum/conf/reputation_changes.py deleted file mode 100644 index cef177f5..00000000 --- a/forum/conf/reputation_changes.py +++ /dev/null @@ -1,149 +0,0 @@ -""" -Settings for reputation changes that apply to -user in response to various actions by the same -users or others -""" -from forum.conf.settings_wrapper import settings -from livesettings import ConfigurationGroup, IntegerValue -from django.utils.translation import ugettext as _ - -REP_CHANGES = ConfigurationGroup( - 'REP_CHANGES', - _('Reputaion loss and gain rules'), - ordering=2 - ) - -settings.register( - IntegerValue( - REP_CHANGES, - 'MAX_REP_GAIN_PER_USER_PER_DAY', - default=200, - description=_('Maximum daily reputation gain per user') - ) -) - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_GAIN_FOR_RECEIVING_UPVOTE', - default=10, - description=_('Gain for receiving an upvote') - ) -) - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_GAIN_FOR_RECEIVING_ANSWER_ACCEPTANCE', - default=15, - description=_('Gain for the author of accepted answer') - ) -) - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_GAIN_FOR_ACCEPTING_ANSWER', - default=2, - description=_('Gain for accepting best answer') - ) -) - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_GAIN_FOR_RECEIVING_DOWNVOTE_CANCELATION', - default=2, - description=_('Gain for post owner on canceled downvote') - ) -) - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_GAIN_FOR_CANCELING_DOWNVOTE', - default=1, - description=_('Gain for voter on canceling downvote') - ) -) -#'gain_by_canceling_downvote', - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_LOSS_FOR_CANCELING_ANSWER_ACCEPTANCE', - default=-2, - description=_('Loss for voter for canceling of answer acceptance') - ) -) -#'lose_by_canceling_accepted_answer', - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_LOSS_FOR_RECEIVING_CANCELATION_OF_ANSWER_ACCEPTANCE', - default=-5, - description=_('Loss for author whose answer was "un-accepted"') - ) -) -#'lose_by_accepted_answer_cancled', - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_LOSS_FOR_DOWNVOTING', - default=-2, - description=_('Loss for giving a downvote') - ) -) -#'lose_by_downvoted', - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_LOSS_FOR_RECEIVING_FLAG', - default=-2, - description=_('Loss for owner of post that was flagged offensive') - ) -) -#'lose_by_flagged', - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_LOSS_FOR_RECEIVING_DOWNVOTE', - default=-1, - description=_('Loss for owner of post that was downvoted') - ) -) -#'lose_by_downvoting', - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_LOSS_FOR_RECEIVING_THREE_FLAGS_PER_REVISION', - default=-30, - description=_('Loss for owner of post that was flagged 3 times per same revision') - ) -) -#'lose_by_flagged_lastrevision_3_times', - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_LOSS_FOR_RECEIVING_FIVE_FLAGS_PER_REVISION', - default=-100, - description=_('Loss for owner of post that was flagged 5 times per same revision') - ) -) -#'lose_by_flagged_lastrevision_5_times', - -settings.register( - IntegerValue( - REP_CHANGES, - 'REP_LOSS_FOR_RECEIVING_UPVOTE_CANCELATION', - default=-10, - description=_('Loss for post owner when upvote is canceled') - ) -) -#'lose_by_upvote_canceled', diff --git a/forum/conf/settings_wrapper.py b/forum/conf/settings_wrapper.py deleted file mode 100644 index abf49e5f..00000000 --- a/forum/conf/settings_wrapper.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Definition of a Singleton wrapper class for livesettings -with interface similar to django.conf.settings -that is each setting has unique key and is accessible -via dotted lookup. - -for example to lookup value of setting BLAH you would do - -from forum.conf import settings - -settings.BLAH - -the value will be taken from livesettings database or cache -note that during compilation phase database is not accessible -for the most part, so actual values are reliably available only -at run time - -livesettings is a module developed for satchmo project -""" -from livesettings import SortedDotDict, config_register - -class ConfigSettings(object): - """A very simple Singleton wrapper for settings - a limitation is that all settings names using this class - must be distinct, even though they might belong - to different settings groups - """ - __instance = None - - def __init__(self): - """assigns SortedDotDict to self.__instance if not set""" - if ConfigSettings.__instance == None: - ConfigSettings.__instance = SortedDotDict() - self.__dict__['_ConfigSettings__instance'] = ConfigSettings.__instance - - def __getattr__(self, key): - """value lookup returns the actual value of setting - not the object - this way only very minimal modifications - will be required in code to convert an app - depending on django.conf.settings to livesettings - """ - return getattr(self.__instance, key).value - - def __setattr__(self, attr, value): - """ settings crutch is read-only in the program """ - raise Exception('ConfigSettings cannot be changed programmatically') - - def register(self, value): - """registers the setting - value must be a subclass of livesettings.Value - """ - key = value.key - if key in self.__instance: - raise Exception('setting %s is already registered' % key) - else: - self.__instance[key] = config_register(value) - -#settings instance to be used elsewhere in the project -settings = ConfigSettings() diff --git a/forum/conf/vote_rules.py b/forum/conf/vote_rules.py deleted file mode 100644 index f249ef53..00000000 --- a/forum/conf/vote_rules.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Forum configuration settings detailing rules on votes -and offensive flags. - -For example number of times a person can vote each day, etc. -""" -from forum.conf.settings_wrapper import settings -from livesettings import ConfigurationGroup, IntegerValue -from django.utils.translation import ugettext as _ - -VOTE_RULES = ConfigurationGroup( - 'VOTE_RULES', - _('Limits applicable to votes and moderation flags'), - ordering=1, - ) - -settings.register( - IntegerValue( - VOTE_RULES, - 'MAX_VOTES_PER_USER_PER_DAY', - default=30, - description=_('Number of votes a user can cast per day') - ) -) - -settings.register( - IntegerValue( - VOTE_RULES, - 'MAX_FLAGS_PER_USER_PER_DAY', - default=5, - description=_('Maximum number of flags per user per day') - ) -) - -settings.register( - IntegerValue( - VOTE_RULES, - 'VOTES_LEFT_WARNING_THRESHOLD', - default=5, - description=_('Threshold for warning about remaining daily votes') - ) -) - -settings.register( - IntegerValue( - VOTE_RULES, - 'MAX_DAYS_TO_CANCEL_VOTE', - default=1, - description=_('Number of days to allow canceling votes') - ) -) - -settings.register( - IntegerValue( - VOTE_RULES, - 'MIN_FLAGS_TO_HIDE_POST', - default=3, - description=_('Number of flags required to automatically hide posts') - ) -) - -settings.register( - IntegerValue( - VOTE_RULES, - 'MIN_FLAGS_TO_DELETE_POST', - default=5, - description=_('Number of flags required to automatically delete posts') - ) -) diff --git a/forum/const/__init__.py b/forum/const/__init__.py new file mode 100644 index 00000000..50676189 --- /dev/null +++ b/forum/const/__init__.py @@ -0,0 +1,137 @@ +# 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, _('not a real question')), + (5, _('the question is answered, right answer was accepted')), + (6, _('question is not relevant or outdated')), + (7, _('question contains offensive or malicious remarks')), + (8, _('spam or advertising')), + (9, _('too localized')), +) + +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'), +) + +#do not translate these!!! +POST_SORT_METHODS = ( + ('latest', _('newest')), + ('oldest', _('oldest')), + ('active', _('active')), + ('inactive', _('inactive')), + ('hottest', _('hottest')), + ('coldest', _('coldest')), + ('mostvoted', _('most voted')), + ('leastvoted', _('least voted')), + ('relevant', _('relevance')), + ) +#todo: add assertion here that all sort methods are unique +#because they are keys to the hash used in implementations of Q.run_advanced_search + +DEFAULT_POST_SORT_METHOD = 'active' +POST_SCOPE_LIST = ( + ('all', _('all')), + ('unanswered', _('unanswered')), + ('favorite', _('favorite')), + ) +DEFAULT_POST_SCOPE = 'all' +PAGE_SIZE_CHOICES = (('10','10',),('30','30',),('50','50',),) + +UNANSWERED_QUESTION_MEANING_CHOICES = ( + ('NO_ANSWERS', _('Question has no answers')), + ('NO_ACCEPTED_ANSWERS', _('Question has no accepted answers')), +) +#todo: implement this +# ('NO_UPVOTED_ANSWERS',), +#) + +#todo: +#this probably needs to be language-specific +#and selectable/changeable from the admin interface +#however it will be hard to expect that people will type +#correct regexes - plus this must be an anchored regex +#to do full string match +TAG_REGEX = r'^[a-z0-9\+\.\-]+$' +TAG_SPLIT_REGEX = r'[ ,]+' + +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'))) +USERS_PAGE_SIZE = 28#todo: move it to settings? + +#an exception import * because that file has only strings +from forum.const.message_keys import * diff --git a/forum/const/message_keys.py b/forum/const/message_keys.py new file mode 100644 index 00000000..c52b9353 --- /dev/null +++ b/forum/const/message_keys.py @@ -0,0 +1,19 @@ +""" +This file must hold keys for translatable messages +that are used as variables +it is important that a dummy _() function is used here +this way message key will be pulled into django.po +and can still be used as a variable in python files +""" +_ = lambda v:v + +#NOTE: all strings must be explicitly put into this dictionary, +#because you don't want to import _ from here with import * +__all__ = ['GREETING_FOR_ANONYMOUS_USER',] + +#this variable is shown in settings, because +#the url within is configurable, the default is reverse('faq') +#if user changes url they will have to be able to fix the +#message translation too +GREETING_FOR_ANONYMOUS_USER = \ + _('First time here? Check out the FAQ!') diff --git a/forum/context.py b/forum/context.py index 043af81d..daa1ee21 100644 --- a/forum/context.py +++ b/forum/context.py @@ -1,21 +1,23 @@ from django.conf import settings +from forum.conf import settings as forum_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, + 'WIKI_ON':forum_settings.WIKI_ON, + 'APP_TITLE' : forum_settings.APP_TITLE, + 'APP_URL' : forum_settings.APP_URL, + 'APP_KEYWORDS' : forum_settings.APP_KEYWORDS, + 'APP_DESCRIPTION': forum_settings.APP_DESCRIPTION, + 'APP_COPYRIGHT': forum_settings.APP_COPYRIGHT, + 'FEEDBACK_SITE_URL': forum_settings.FEEDBACK_SITE_URL, + 'FORUM_ABOUT': forum_settings.FORUM_ABOUT, + 'FORUM_PRIVACY': forum_settings.FORUM_PRIVACY, + 'GOOGLE_SITEMAP_CODE':forum_settings.GOOGLE_VERIFICATION_CODE, + 'GOOGLE_ANALYTICS_KEY':forum_settings.GOOGLE_ANALYTICS_KEY, + 'RESOURCE_REVISION':settings.RESOURCE_REVISION, + 'ASKBOT_SKIN':settings.ASKBOT_DEFAULT_SKIN, 'EMAIL_VALIDATION': settings.EMAIL_VALIDATION, - 'FEEDBACK_SITE_URL': settings.FEEDBACK_SITE_URL, 'FORUM_SCRIPT_ALIAS': settings.FORUM_SCRIPT_ALIAS, 'LANGUAGE_CODE': settings.LANGUAGE_CODE, - 'GOOGLE_SITEMAP_CODE':settings.GOOGLE_SITEMAP_CODE, - 'GOOGLE_ANALYTICS_KEY':settings.GOOGLE_ANALYTICS_KEY, - 'WIKI_ON':settings.WIKI_ON, - 'RESOURCE_REVISION':settings.RESOURCE_REVISION, - 'ASKBOT_SKIN':settings.ASKBOT_DEFAULT_SKIN, 'EDITABLE_SCREEN_NAME':settings.EDITABLE_SCREEN_NAME, } return {'settings':my_settings} diff --git a/forum/doc/HOW_TO_DEBUG b/forum/doc/HOW_TO_DEBUG new file mode 100644 index 00000000..fbbdb1f7 --- /dev/null +++ b/forum/doc/HOW_TO_DEBUG @@ -0,0 +1,39 @@ +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 Askbot 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 'askbot\/django_authopenid' log/django.askbot.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 +askbot 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/forum/doc/INSTALL b/forum/doc/INSTALL new file mode 100644 index 00000000..73714566 --- /dev/null +++ b/forum/doc/INSTALL @@ -0,0 +1,288 @@ +CONTENTS +------------------ +A. PREREQUISITES +B. INSTALLATION + 1. Settings file + 2. Database + 3. Running Askbot 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 +----------------------------------------------- +Note: by default all installation activity is made in the superuser 'root' account. +This can be achieved either by logging in as root (su root), +or - if you have program sudo installed - prefix all commands with sudo. +So sodo will be listed below as optional. + +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, Django v1.1.1 + +1A If you are using MySQL, mysql client for python must be 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 + +11. South +http://south.aeracode.org/docs/installation.html +Used for database schema and data migrations +[sudo] easy_install South + +EXTRA DEPENDENCIES FOR PYTHON 2.4 +* hashlib (made standard in python 2.5) + +NOTES: django_authopenid is included into Askbot code +and is significantly modified. http://code.google.com/p/django-authopenid/ +no need to install this library + +B. INSTALLATION + +NOTE: If you want to upgrade software, not install from scratch - + take a look into forum/documentation/UPGRADE + +----------------------------------------------- +0. Make sure you have all above python libraries installed. + + DO NOT name the main directory 'askbot' - this name is reserved + for the future name of the app file itself. + + make askbot installation server-readable on Linux command might be: + chown -R yourlogin:apache /path/to/askbot-site + + directories: + /path/to/askbot-site/forum/upfiles + /path/to/askbot-site/log + must be server writable + + on Linux type chmod + chmod -R g+w /path/to/askbot-site/forum/upfiles + chmod -R g+w /path/to/askbot-site/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. + +Minimally required modification of settings_local.py are +DATABASE_NAME +DATABASE_USER +DATABASE_PASSWORD +DATABASE_ENGINE + +If you set these up, and your database is ready (see section 2), +run (note that application 'forum' is under control of south migration system: + +python manage.py syncdb #create tables for anything not under control of migration system +python manage.py migrate forum #run migration command - will apply all migrations in sequence + +Now you are ready to test your installation: + +python manage.py runserver `hostname -i`:8000 +(choose another port number if you wish) + +and askbot should be running - if you have any issues at this point (or later:) +please post them at http://askbot.org/meta + +2. Database + +Prepare your database by using the same database/account +configuration from above. + +If your host has database manager in the control panel - you +can use that or you can create database by typing commands manually + +on MySQL the commands are: + +create database askbot DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci; +grant all on askbot.* to 'askbot'@'localhost'; + +And then run : + +python manage.py syncdb +python manage.py migrate + +3. Running Askbot 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 + +The main wsgi script is in the file django.wsgi +it does not need to be modified + +4.1 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/askbot-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 askbot + WSGIProcessGroup askbot + + #force all content to be served as static files + #otherwise django will be crunching images through itself wasting time + Alias /m/ /path/to/askbot-site/forum/skins/ + Alias /upfiles/ /path/to/askbot-site/forum/upfiles/ + + Order deny,allow + Allow from all + + + #this is your wsgi script described in the prev section + WSGIScriptAlias / /path/to/askbot-site/django.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/askbot/access_log common + ErrorLog /var/log/httpd/askbot/error_log + +#(optional) run admin interface under https + + ServerAdmin forum@example.com + DocumentRoot /path/to/askbot-site + ServerName example.com + SSLEngine on + SSLCertificateFile /path/to/ssl-certificate/server.crt + SSLCertificateKeyFile /path/to/ssl-certificate/server.key + WSGIScriptAlias / /path/to/askbot-site/django.wsgi + CustomLog /var/log/httpd/askbot/access_log common + ErrorLog /var/log/httpd/askbot/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 askbot index first time manually + + % indexer --config /path/to/sphinx.conf --index askbot + + 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 forum/cron/send_email_alerts + set up a cron job to call forum/cron/send_email_alerts once or twice a day + subscription sender may be tested manually in shell + by calling forum/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 forum/sql_scripts folder, +including badges and test accounts for CNProg.com. You +don't need them to run your sample. + +C. 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 + "Askbot" in the msgstr lines +* skins diff --git a/forum/doc/INSTALL.pip b/forum/doc/INSTALL.pip new file mode 100644 index 00000000..2f817ff8 --- /dev/null +++ b/forum/doc/INSTALL.pip @@ -0,0 +1,31 @@ +* Install virtualenv and pip: + easy_install virtualenv + easy_install pip + +* Install MySQL: + sudo apt-get install mysql-client mysql-server + +* Install sphinxsearch. See: + [optional] + http://sphinxsearch.com/downloads.html + http://www.hackido.com/2009/01/install-sphinx-search-on-ubuntu.html + git://github.com/johnl/deb-sphinx-search.git + +* Install a virtual environment OUTSIDE of this directory: + pip install -E ~/env -r askbot-requirements.txt +[there is discussion on the pinax forums about what it should be outside +the source directory] + +* 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 + +* Start your environment: + source ~/env/bin/activate + +* Install mysql-python into your virtualenv, because we can't +automagically install it with pip: + easy_install --prefix ~/env/ mysql-python + +For more information about why pip can't automatically install the +MySQL driver, see this message: http://groups.google.com/group/python-virtualenv/msg/ea988085951c92b3 diff --git a/forum/doc/INSTALL.webfaction b/forum/doc/INSTALL.webfaction new file mode 100644 index 00000000..a449ffe6 --- /dev/null +++ b/forum/doc/INSTALL.webfaction @@ -0,0 +1,345 @@ +NOTE: this file may be a bit out of date + +Detailed instructions for installing Askbot on WebFaction + +Adapted from http://code.pinaxproject.com/wiki/DetailedPinaxWebfaction/ + +Please email turian at gmail with any updates or corrections. + + +Installing Askbot on Webfaction +------------------------------------ + +Details the steps for setting up Askbot on a Webfaction shared-hosting +account, including email setup, using Apache2, mod_wsgi, psycopg2. + +If you want to search-and-replace through this file, you will need to replace: + askbot_server [name of Webfaction application, which will be in ~/webapps/] + askbot_static [name of Webfaction application for static media serving] + DOMAIN.com [domain name for Askbot site] + PORT [port number assigned by WebFaction to your mod_wsgi application] + SITENAME [name you give the Askbot site, which will contain the apache logs] + myaskbot [name of the Askbot project] + MAILBOX_USERNAME [username you give the email address] + MAILBOX_PASSWORD [password that webfaction gives to this email username] + AskbotDATABASE_NAME [username you give the database] + AskbotDATABASE_PASSWORD [password that webfaction gives to this database] + ~/envs/askbot [directory for the Askbot python environment, grep for 'env'] + USERNAME [your WebFaction username] + +Some things I'm not sure about: + +Here's what I don't know how to do: + * Set up a nginx server for static media. + * Configure sphinx search + * Use PostgreSQL, not MySQL: http://askbot.net/question/13/can-i-use-askbot-with-postgresql + + +Webfaction Control Panel +-------------------------- + +(Note: if you sign up and pick django it will create the application +for you, website/subdomain and associate the two for you.) + + If necessary, add or create any domains or subdomains you may need. + + https://panel.webfaction.com/domain/list/ + + Let's call the domain DOMAIN.com. + + Create a new Webfaction application with a "Type:" of "mod_wsgi + 2.5/Python2.5", naming it "askbot_server". (These instructions + might also work with mod_wsgi 2.0, if you like.) + + https://panel.webfaction.com/app_/list + + Note the port number assigned to the mod_wsgi application. Call + it PORT. + + Create a new Webfaction website which will associate the subdomain + with the new askbot_server application. Give it name SITENAME, at least one + domain, and set it to use the askbot_server application for the site's + root location, "/". + + https://panel.webfaction.com/site/list + + You will need to create a database, typically one for each project + you create. Change the type to PostgreSql and modify the name (it + defaults to your webfaction account name) by adding an underscore + and a project-specific identifier such as "_askbot". Before + leaving this section of the control panel, you may wish to change + the password. + + https://panel.webfaction.com/database/create + + Call these AskbotDATABASE_NAME and AskbotDATABASE_PASSWORD. + + Save the database password for later. + + [The following I haven't figured out yet] + You will probably want to add a static media server. This is a + Webfaction application. I created one of type "Static only (no + .htaccess)" and with the name of "askbot_static". + + https://panel.webfaction.com/app_/create + + To configure email, you need an email mailbox. Add one here. Note + that your mailbox password shouldn't be the same password you use + to SSH to webfaction. + + https://panel.webfaction.com/mailbox/list + + Save the mail password for later. + We will call the username and password MAILBOX_USERNAME and + MAILBOX_PASSWORD, respectively. + You might also consider adding an email address like admin@DOMAIN.com, + here: + + https://panel.webfaction.com/email/list + + +Askbot Software +-------------- + + Log onto webfaction and get the code. I use my fork because I have + a simple pip installation: + git://github.com/turian/askbot.git + In my situation, I keep source code in ~/utils/src, create + virtual environments in ~/envs/askbot, and create Pinax projects in + ~/webapps/askbot_server/projects. + + You will need pip + virtualenv installed: + + easy_install --prefix=~/utils/ pip + easy_install --prefix=~/utils/ virtualenv + + cd ~/utils/src/ + git clone git://github.com/turian/askbot.git + cd askbot + + # We need python2.5 to be compatible with WSGI + python2.5 ~/utils/bin/pip install -E ~/envs/askbot -r askbot-requirements.txt + source ~/envs/askbot/bin/activate + + # [Optional] If you want a MySQL database + easy_install-2.5 --prefix ~/envs/askbot/ mysql-python + +Additional Software +------------------- + + [Note that PostgreSQL installation doesn't work for me.] + + You will need to install psycopg2 separately for PostgreSQL. + Psycopg2 requires a little fiddling. Continuing to + work in the ~/utils/src/ directory: + + cd ~/utils/src/ + wget http://initd.org/pub/software/psycopg/psycopg2-2.0.13.tar.gz + tar zxf psycopg2-2.0.13.tar.gz + cd psycopg2-2.0.13 + nano setup.cfg + + # edit the line reading "#pg_config=" so that it reads: + "pg_config=/usr/local/pgsql/bin/pg_config" + + python2.5 setup.py build + python2.5 setup.py install + + +Create a Project +---------------- + + In Pinax, you clone a project from Askbot. + However, Askbot we just copy it. + + cd ~/webapps/askbot_server + mkdir projects + cd projects + cp -R ~/utils/src/askbot myaskbot + cd myaskbot + export AskbotPROJECT=`pwd` + + Make some directories, as described in the Askbot INSTALL file: + [okay I haven't actually done this yet] + +# mkdir -p $AskbotSITE/upfiles/ +# mkdir -p $AskbotLOG +# sudo chown -R `whoami`:www-data $AskbotSITE +# sudo chown -R `whoami`:www-data $AskbotLOG +# chmod -R g+w $AskbotSITE/upfiles +# chmod -R g+w $AskbotLOG + + + Edit the settings files: + + cd $AskbotPROJECT + cp settings_local.py.dist settings_local.py + vi settings_local.py settings.py + + Pay attention to the following settings: + + DATABASE_ENGINE = 'mysql' + DATABASE_NAME = 'AskbotDATABASE_NAME' + DATABASE_USER = 'AskbotDATABASE_NAME' + DATABASE_PASSWORD = 'AskbotDATABASE_PASSWORD' + + EMAIL_HOST='smtp.webfaction.com' + EMAIL_HOST_USER='MAILBOX_USERNAME' + EMAIL_HOST_PASSWORD='MAILBOX_PASSWORD' + EMAIL_PORT='25' + DEFAULT_FROM_EMAIL = 'MAILBOX_USERNAME@DOMAIN.com' + SERVER_EMAIL = 'MAILBOX_USERNAME@DOMAIN.com' + # The following setting might not be necessary, it's used in Pinax tho + CONTACT_EMAIL = "MAILBOX_USERNAME@DOMAIN.com" + + APP_URL = 'http://DOMAIN.com' #used by email notif system and RSS + + [Later on, the install instructions should talk about] + SERVE_MEDIA = False # [Not present, not ready yet] + + Create a directory for logs: + + cd $AskbotPROJECT + mkdir log + + Modify mail cron scripts "cron/send_email_alerts" as follows: + [Pinax has cron/emit_notices.sh, cron/retry_deferred.sh, + cron/send_mail.sh, are these also necessary?] + + #!/bin/sh + + WORKON_HOME=~/envs/askbot + PROJECT_ROOT=~/webapps/askbot_server/projects/myaskbot/ + + # activate virtual environment + . $WORKON_HOME/bin/activate + + cd $PROJECT_ROOT + python manage.py send_email_alerts >> $PROJECT_ROOT/log/cron_mail.log 2>&1 + + Use command "crontab -e" to add this script to your cron file, to run twice a day:: + + 1 0,12 * * * ~/webapps/askbot_server/projects/myaskbot/cron/send_email_alerts + + [Configure sphinx] + + Create the database tables, indices, and so forth: + + python manage.py syncdb + + [Ignore the following static media steps, I haven't tried them] + Build media directory links within the project and create symbolic + links on the static media server. + python manage.py build_media -all + mkdir ~/webapps/Askbot_STATIC/myaskbot + ln -sd ~/webapps/askbot_server/projects/myaskbot/site_media ~/webapps/Askbot_STATIC/myaskbot/site_media + + + Set up the badges: + + 1. You should run the SQL commands in: + + sql_scripts/badges.sql + + 2. Edit paths in the file `cron/multi_award_badges`. (This + file doesn't yet exist in the git repositories, so just + copy `cron/send_email_alerts` and make sure the command + `multi_award_badges` is executed.) + + 3. Run `cron/multi_award_badges` to make sure it works okay. + + 4. Use `crontab -e` to call `cron/multi_award_badges` maybe + four times an hour. + + 4,19,34,49 * * * * ~/webapps/askbot_server/projects/myaskbot/cron/multi_award_badges + + 5. Repeat steps 1-4 for `cron/once_award_badges`. + + +Configure Apache2 +---------------- + + Edit ~/webapps/askbot_server/apache2/conf/httpd.conf as follows:: + + ServerAdmin "MAILBOX_USERNAME@DOMAIN.com" + ServerRoot "/home/USERNAME/webapps/askbot_server/apache2" + ServerName DOMAIN.com + + LoadModule dir_module modules/mod_dir.so + LoadModule env_module modules/mod_env.so + #LoadModule setenvif_module modules/mod_setenvif.so + LoadModule log_config_module modules/mod_log_config.so + LoadModule mime_module modules/mod_mime.so + LoadModule rewrite_module modules/mod_rewrite.so + LoadModule wsgi_module modules/mod_wsgi.so + + KeepAlive Off + Listen PORT + LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + CustomLog /home/USERNAME/logs/user/access_askbot_server_log combined + ErrorLog /home/USERNAME/logs/user/error_askbot_server_log + ServerLimit 2 + + #SetEnvIf X-Forwarded-SSL on HTTPS=1 + + WSGIPythonPath /home/USERNAME/envs/askbot/lib/python2.5/site-packages/ + WSGIScriptAlias / /home/USERNAME/webapps/askbot_server/projects/myaskbot/django.wsgi + + LoadModule alias_module modules/mod_alias.so + WSGIDaemonProcess askbotWSGI user=USERNAME group=USERNAME threads=25 python-path=/home/USERNAME/envs/askbot/lib/python2.5/site-packages + WSGIProcessGroup askbotWSGI + + NameVirtualHost 127.0.0.1:PORT + + #ErrorLog "logs/myaskbot_2009_05_06.log" + SetHandler none + #Alias /site_media /home/USERNAME/webapps/static/myaskbot/site_media + + #force all content to be served as static files + #otherwise django will be crunching images through itself wasting time + Alias /content/ /home/USERNAME/webapps/askbot_server/projects/myaskbot/templates/content/ + Alias /forum/admin/media/ /home/turian/envs/askbot/lib/python2.5/site-packages/django/contrib/admin/media/ + #AliasMatch /([^/]*\.css) /home/USERNAME/webapps/askbot_server/projects/myaskbot/templates/content/style/$1 + + # Order deny,allow + # Allow from all + + + If you want virtual hosts of the admin interface under HTTPS, please + look at Askbot's install file. + + Edit ~/webapps/askbot_server/projects/myaskbot/deploy/django.wsgi as follows:: + + import os + import sys + + # redirect sys.stdout to sys.stderr for bad libraries like geopy that uses + # print statements for optional import exceptions. + sys.stdout = sys.stderr + + from os.path import abspath, dirname, join + from site import addsitedir + + # add the virtual environment site-packages to the path + from site import addsitedir + addsitedir('/home/USERNAME/envs/askbot/lib/python2.5/site-packages') + + sys.path.insert(0, abspath(join(dirname(__file__), "../"))) + sys.path.append(abspath(dirname(__file__))) + + from django.conf import settings + os.environ["DJANGO_SETTINGS_MODULE"] = "myaskbot.settings" + + #print sys.path + + from django.core.handlers.wsgi import WSGIHandler + application = WSGIHandler() + +And then you're up and running with: + + ~/webapps/askbot_server/apache2/bin/stop + ~/webapps/askbot_server/apache2/bin/start + +You should log in to the admin interface (http://DOMAIN.com/admin/), +and go to "Sites > Sites", and change the domain name that is used in +all emails. diff --git a/forum/doc/ROADMAP.rst b/forum/doc/ROADMAP.rst new file mode 100644 index 00000000..c79e0ae4 --- /dev/null +++ b/forum/doc/ROADMAP.rst @@ -0,0 +1,88 @@ +Intro +========= +ROADMAP aims to streamline activities of the Askbot open source project and +to minimize ad-hoc approaches of "big-picture" level. + +Aksbot is a Question and Asnwer system for the normal people! + +Let's discuss stuff that goes into this file on +http://groups.google.com/group/askbot + +Bacic principles of the project +================================== +Here they are: + +* our rule #1 is that all developers have commit right to the project + repository, but they must follow this ROADMAP and TODO - + to keep up with our own sanity. +* we welcome contributions by other people and show tolerance + and patience - especially to the new team members. +* when users who might not be tech-savvy ask questions - + we try to answer to the point and using their language + (i.e. not programmer jargon:) +* we favor plain and minimalistic style of programming, but pay + attention to detail - especially details of user experience. + +We try do develop using the following workflow: + +* specify problem that we try to solve +* create requirements that will guarantee a solution, once met +* dream up some implementation ideas (maybe even some sketches on the paper) +* discuss and decide on the best one +* write and test code + +The process doesn't have to be this formal all the time, but trying to stick +to some subset of this almost always helps! +Especially it helps to iron out disagreements between +individual programmers (which if you are one - you know are qute common +- and they don't all end well :). + +Ad-hoc programming - i.e. simply go and add code - is not really encouraged. +This works fine in the one person team or when the team consists of +best friends, but is almost sure to fail in a heterogenous group. + +Architecture and Requirements +===================================== +Obviously Django and Python are pre-made choices - so this +is not going to change any time soon. At this point all of +the client side Javascript is written using jQuery library. + +Our basic principle is that Askbot should be a mashable Q&A component. +Askbot is an application written in Python/Django. So it should be +distributable as a Django App alone or as a whole site (by option). + +If we develop sub-systems that can be used in the broader scope - +we package that thing as a separate django application (login system is one example). + +We will start using Google Closure library soon! + +Sub-systems +----------------- +* authentication system +* Q&A system +* admin interface +* full text search +* skins (directory forum/skins) + +Authentication system +------------------------- +Authentication system will be a separate django application + +Here is the discussion thread: +* http://groups.google.com/group/askbot/browse_thread/thread/1916dfcf666dd56c + +Most of the requirements are listed in the first message + +Skins +----------- +Skins eventually must be upgrade-stable - that is people who created custom +skins should not need to change anything if something changes in the code + +Admin interface +----------------------- +* extend forum/settings.py to list default settings of various groups +* create Registry database table the will store setting values +* leave only essential settings that go to the main django settings.py +Create key-value storage +* should some settings be accessible to admins and some to staff??? + for example-secret keys probably should not be shared with staff members diff --git a/forum/doc/TODO.rst b/forum/doc/TODO.rst new file mode 100644 index 00000000..b89013b0 --- /dev/null +++ b/forum/doc/TODO.rst @@ -0,0 +1,70 @@ +note: there is also WISH_LIST. Here is only stuff that will be done soon. + +Site looks +=========== +* make links within posts underlined +* add happy talk to tags and people view + +Code Cleanups +============== +* remove usage of EXTERNAL_LEGACY_LOGIN +* clean up forum_modules: + * keep this directory for dependency modules that can be shared + by multiple apps, + * but move other things that are not shared + inside forum app directory + * one-by one convert "auto-discovery" modules into + regular explicit python imports +* python2.4 incompatibilities + * datatime.datetime.strptime + +Search +======== +* make full text search work in postgres +* walkthrough sphinx search setup again +* text query lost when visiting question, it just doesn't print in Q view +* make username and tags persist in corresponding search windows + +Bugs +====== +* fix skin resolution bug in javascript - currently have to + copy skin media to customize + +Refactoring +============= +nothing right now + +Skins +======= +* organize templates and document them so that + skins could be more easily created by others + who are savvy enough +* identify and maybe create snippet-type templates + and put them into a separate directory + for example: + * gravatar (currently a string in + forum/templatetags/extra_tags.py - try inclusion template + and see if it slows things down a lot) + * question body + * answer body + * datetime widget??? +* there is a separator line between posts + but it shows either before the post or after + it is nice that separator is lightweight - + based on css alone - but we need to fix it so that + it shows only between the posts as a joining item + +Features +=========== +* new login system, please see + http://groups.google.com/group/askbot/browse_thread/thread/1916dfcf666dd56c + on a separate branch multi-auth-app, then merge +* forum admin interface, some badge configuration + +Development environment +========================== +* set up environment for closure development + +Project website +==================== +* Adopt Jekyll for project site and transition from Dango diff --git a/forum/doc/UPGRADE b/forum/doc/UPGRADE new file mode 100644 index 00000000..538b75a0 --- /dev/null +++ b/forum/doc/UPGRADE @@ -0,0 +1,24 @@ +if you are upgrading this software, then + +* first download the newer version and write it over the old one. + +for the database migrations you will need to use django package called "south" + +Install it (if you don't have it yet) with: + + easy_install South + +* 'south' must already be in the list of INSTALLED_APPS + otherwise you must have downloaded wrong version of Askbot + +if you are using south the very first time, then type: + + python manage.py migrate forum 0001_initial --fake + +otherwise skip above step. + +Finally run + + python manage.py migrate forum --auto + +then all relevant schema and data migrations will be applied diff --git a/forum/doc/WISH_LIST b/forum/doc/WISH_LIST new file mode 100644 index 00000000..2b53662c --- /dev/null +++ b/forum/doc/WISH_LIST @@ -0,0 +1,55 @@ +* smarter debug mode +* The wonder bar (integrated the search / ask functionality) +* The authentication system ??? +* allow multiple logins to the same account +* allow multiple logins to the same account +* more advanced templating/skinning system +* per-tag email subscriptions +* view for personalized news on the site +* a little flag popping when there are news +* drill-down mode for navigation by tags +* improved admin console +* sort out mess with profile - currently we patch django User + +* Some functionality should be moved out of the forums app, in the case +that the forum app is restricted only to authenticated users: + + (r'^%s/$' % _('signin/'), 'django_authopenid.views.signin'), + url(r'^%s$' % _('about/'), app.about, name='about'), + url(r'^%s$' % _('faq/'), app.faq, name='faq'), + url(r'^%s$' % _('privacy/'), app.privacy, name='privacy'), + url(r'^%s$' % _('logout/'), app.logout, name='logout'), + url(r'^%s$' % _('feedback/'), app.feedback, name='feedback'), + (r'^%sfb/' % _('account/'), include('fbconnect.urls')), + (r'^%s' % _('account/'), include('django_authopenid.urls')), + +Copied from old todo list: + +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 --> askbot + without breaking things and keeping name of the project root + named the same way - askbot + +==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/forum/doc/askbot-requirements.txt b/forum/doc/askbot-requirements.txt new file mode 100644 index 00000000..66a37fbe --- /dev/null +++ b/forum/doc/askbot-requirements.txt @@ -0,0 +1,9 @@ +django>=1.1 # Note: email subscription sender job requires Django 1.1, everything else works with 1.0 +#mysql-python # Can't use with pip, see http://groups.google.com/group/python-virtualenv/msg/ea988085951c92b3 +python-openid +html5lib +markdown2 +git+git://github.com/robhudson/django-debug-toolbar.git +git+git://github.com/dcramer/django-sphinx.git +svn+http://recaptcha-django.googlecode.com/svn/trunk/ +svn+http://recaptcha.googlecode.com/svn/trunk/recaptcha-plugins/python/ diff --git a/forum/doc/scratch b/forum/doc/scratch new file mode 100644 index 00000000..948055fa --- /dev/null +++ b/forum/doc/scratch @@ -0,0 +1,12 @@ +Error upon restarting Apache: + +Name duplicates previous WSGI daemon definition + +different keys - empty space counts for translation keys +{% blocktrans %}page number {{num}} {% endblocktrans %} +{% blocktrans %}page number {{num}}{% endblocktrans %} + +for admin interface downloaded two packages: +django-keyedcache +django-livesettings +from http://bitbucket.org/bkroeze/ diff --git a/forum/documentation/HOW_TO_DEBUG b/forum/documentation/HOW_TO_DEBUG deleted file mode 100644 index fbbdb1f7..00000000 --- a/forum/documentation/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 Askbot 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 'askbot\/django_authopenid' log/django.askbot.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 -askbot 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/forum/documentation/INSTALL b/forum/documentation/INSTALL deleted file mode 100644 index 73714566..00000000 --- a/forum/documentation/INSTALL +++ /dev/null @@ -1,288 +0,0 @@ -CONTENTS ------------------- -A. PREREQUISITES -B. INSTALLATION - 1. Settings file - 2. Database - 3. Running Askbot 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 ------------------------------------------------ -Note: by default all installation activity is made in the superuser 'root' account. -This can be achieved either by logging in as root (su root), -or - if you have program sudo installed - prefix all commands with sudo. -So sodo will be listed below as optional. - -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, Django v1.1.1 - -1A If you are using MySQL, mysql client for python must be 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 - -11. South -http://south.aeracode.org/docs/installation.html -Used for database schema and data migrations -[sudo] easy_install South - -EXTRA DEPENDENCIES FOR PYTHON 2.4 -* hashlib (made standard in python 2.5) - -NOTES: django_authopenid is included into Askbot code -and is significantly modified. http://code.google.com/p/django-authopenid/ -no need to install this library - -B. INSTALLATION - -NOTE: If you want to upgrade software, not install from scratch - - take a look into forum/documentation/UPGRADE - ------------------------------------------------ -0. Make sure you have all above python libraries installed. - - DO NOT name the main directory 'askbot' - this name is reserved - for the future name of the app file itself. - - make askbot installation server-readable on Linux command might be: - chown -R yourlogin:apache /path/to/askbot-site - - directories: - /path/to/askbot-site/forum/upfiles - /path/to/askbot-site/log - must be server writable - - on Linux type chmod - chmod -R g+w /path/to/askbot-site/forum/upfiles - chmod -R g+w /path/to/askbot-site/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. - -Minimally required modification of settings_local.py are -DATABASE_NAME -DATABASE_USER -DATABASE_PASSWORD -DATABASE_ENGINE - -If you set these up, and your database is ready (see section 2), -run (note that application 'forum' is under control of south migration system: - -python manage.py syncdb #create tables for anything not under control of migration system -python manage.py migrate forum #run migration command - will apply all migrations in sequence - -Now you are ready to test your installation: - -python manage.py runserver `hostname -i`:8000 -(choose another port number if you wish) - -and askbot should be running - if you have any issues at this point (or later:) -please post them at http://askbot.org/meta - -2. Database - -Prepare your database by using the same database/account -configuration from above. - -If your host has database manager in the control panel - you -can use that or you can create database by typing commands manually - -on MySQL the commands are: - -create database askbot DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci; -grant all on askbot.* to 'askbot'@'localhost'; - -And then run : - -python manage.py syncdb -python manage.py migrate - -3. Running Askbot 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 - -The main wsgi script is in the file django.wsgi -it does not need to be modified - -4.1 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/askbot-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 askbot - WSGIProcessGroup askbot - - #force all content to be served as static files - #otherwise django will be crunching images through itself wasting time - Alias /m/ /path/to/askbot-site/forum/skins/ - Alias /upfiles/ /path/to/askbot-site/forum/upfiles/ - - Order deny,allow - Allow from all - - - #this is your wsgi script described in the prev section - WSGIScriptAlias / /path/to/askbot-site/django.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/askbot/access_log common - ErrorLog /var/log/httpd/askbot/error_log - -#(optional) run admin interface under https - - ServerAdmin forum@example.com - DocumentRoot /path/to/askbot-site - ServerName example.com - SSLEngine on - SSLCertificateFile /path/to/ssl-certificate/server.crt - SSLCertificateKeyFile /path/to/ssl-certificate/server.key - WSGIScriptAlias / /path/to/askbot-site/django.wsgi - CustomLog /var/log/httpd/askbot/access_log common - ErrorLog /var/log/httpd/askbot/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 askbot index first time manually - - % indexer --config /path/to/sphinx.conf --index askbot - - 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 forum/cron/send_email_alerts - set up a cron job to call forum/cron/send_email_alerts once or twice a day - subscription sender may be tested manually in shell - by calling forum/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 forum/sql_scripts folder, -including badges and test accounts for CNProg.com. You -don't need them to run your sample. - -C. 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 - "Askbot" in the msgstr lines -* skins diff --git a/forum/documentation/INSTALL.pip b/forum/documentation/INSTALL.pip deleted file mode 100644 index 2f817ff8..00000000 --- a/forum/documentation/INSTALL.pip +++ /dev/null @@ -1,31 +0,0 @@ -* Install virtualenv and pip: - easy_install virtualenv - easy_install pip - -* Install MySQL: - sudo apt-get install mysql-client mysql-server - -* Install sphinxsearch. See: - [optional] - http://sphinxsearch.com/downloads.html - http://www.hackido.com/2009/01/install-sphinx-search-on-ubuntu.html - git://github.com/johnl/deb-sphinx-search.git - -* Install a virtual environment OUTSIDE of this directory: - pip install -E ~/env -r askbot-requirements.txt -[there is discussion on the pinax forums about what it should be outside -the source directory] - -* 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 - -* Start your environment: - source ~/env/bin/activate - -* Install mysql-python into your virtualenv, because we can't -automagically install it with pip: - easy_install --prefix ~/env/ mysql-python - -For more information about why pip can't automatically install the -MySQL driver, see this message: http://groups.google.com/group/python-virtualenv/msg/ea988085951c92b3 diff --git a/forum/documentation/INSTALL.webfaction b/forum/documentation/INSTALL.webfaction deleted file mode 100644 index a449ffe6..00000000 --- a/forum/documentation/INSTALL.webfaction +++ /dev/null @@ -1,345 +0,0 @@ -NOTE: this file may be a bit out of date - -Detailed instructions for installing Askbot on WebFaction - -Adapted from http://code.pinaxproject.com/wiki/DetailedPinaxWebfaction/ - -Please email turian at gmail with any updates or corrections. - - -Installing Askbot on Webfaction ------------------------------------- - -Details the steps for setting up Askbot on a Webfaction shared-hosting -account, including email setup, using Apache2, mod_wsgi, psycopg2. - -If you want to search-and-replace through this file, you will need to replace: - askbot_server [name of Webfaction application, which will be in ~/webapps/] - askbot_static [name of Webfaction application for static media serving] - DOMAIN.com [domain name for Askbot site] - PORT [port number assigned by WebFaction to your mod_wsgi application] - SITENAME [name you give the Askbot site, which will contain the apache logs] - myaskbot [name of the Askbot project] - MAILBOX_USERNAME [username you give the email address] - MAILBOX_PASSWORD [password that webfaction gives to this email username] - AskbotDATABASE_NAME [username you give the database] - AskbotDATABASE_PASSWORD [password that webfaction gives to this database] - ~/envs/askbot [directory for the Askbot python environment, grep for 'env'] - USERNAME [your WebFaction username] - -Some things I'm not sure about: - -Here's what I don't know how to do: - * Set up a nginx server for static media. - * Configure sphinx search - * Use PostgreSQL, not MySQL: http://askbot.net/question/13/can-i-use-askbot-with-postgresql - - -Webfaction Control Panel --------------------------- - -(Note: if you sign up and pick django it will create the application -for you, website/subdomain and associate the two for you.) - - If necessary, add or create any domains or subdomains you may need. - - https://panel.webfaction.com/domain/list/ - - Let's call the domain DOMAIN.com. - - Create a new Webfaction application with a "Type:" of "mod_wsgi - 2.5/Python2.5", naming it "askbot_server". (These instructions - might also work with mod_wsgi 2.0, if you like.) - - https://panel.webfaction.com/app_/list - - Note the port number assigned to the mod_wsgi application. Call - it PORT. - - Create a new Webfaction website which will associate the subdomain - with the new askbot_server application. Give it name SITENAME, at least one - domain, and set it to use the askbot_server application for the site's - root location, "/". - - https://panel.webfaction.com/site/list - - You will need to create a database, typically one for each project - you create. Change the type to PostgreSql and modify the name (it - defaults to your webfaction account name) by adding an underscore - and a project-specific identifier such as "_askbot". Before - leaving this section of the control panel, you may wish to change - the password. - - https://panel.webfaction.com/database/create - - Call these AskbotDATABASE_NAME and AskbotDATABASE_PASSWORD. - - Save the database password for later. - - [The following I haven't figured out yet] - You will probably want to add a static media server. This is a - Webfaction application. I created one of type "Static only (no - .htaccess)" and with the name of "askbot_static". - - https://panel.webfaction.com/app_/create - - To configure email, you need an email mailbox. Add one here. Note - that your mailbox password shouldn't be the same password you use - to SSH to webfaction. - - https://panel.webfaction.com/mailbox/list - - Save the mail password for later. - We will call the username and password MAILBOX_USERNAME and - MAILBOX_PASSWORD, respectively. - You might also consider adding an email address like admin@DOMAIN.com, - here: - - https://panel.webfaction.com/email/list - - -Askbot Software --------------- - - Log onto webfaction and get the code. I use my fork because I have - a simple pip installation: - git://github.com/turian/askbot.git - In my situation, I keep source code in ~/utils/src, create - virtual environments in ~/envs/askbot, and create Pinax projects in - ~/webapps/askbot_server/projects. - - You will need pip + virtualenv installed: - - easy_install --prefix=~/utils/ pip - easy_install --prefix=~/utils/ virtualenv - - cd ~/utils/src/ - git clone git://github.com/turian/askbot.git - cd askbot - - # We need python2.5 to be compatible with WSGI - python2.5 ~/utils/bin/pip install -E ~/envs/askbot -r askbot-requirements.txt - source ~/envs/askbot/bin/activate - - # [Optional] If you want a MySQL database - easy_install-2.5 --prefix ~/envs/askbot/ mysql-python - -Additional Software -------------------- - - [Note that PostgreSQL installation doesn't work for me.] - - You will need to install psycopg2 separately for PostgreSQL. - Psycopg2 requires a little fiddling. Continuing to - work in the ~/utils/src/ directory: - - cd ~/utils/src/ - wget http://initd.org/pub/software/psycopg/psycopg2-2.0.13.tar.gz - tar zxf psycopg2-2.0.13.tar.gz - cd psycopg2-2.0.13 - nano setup.cfg - - # edit the line reading "#pg_config=" so that it reads: - "pg_config=/usr/local/pgsql/bin/pg_config" - - python2.5 setup.py build - python2.5 setup.py install - - -Create a Project ----------------- - - In Pinax, you clone a project from Askbot. - However, Askbot we just copy it. - - cd ~/webapps/askbot_server - mkdir projects - cd projects - cp -R ~/utils/src/askbot myaskbot - cd myaskbot - export AskbotPROJECT=`pwd` - - Make some directories, as described in the Askbot INSTALL file: - [okay I haven't actually done this yet] - -# mkdir -p $AskbotSITE/upfiles/ -# mkdir -p $AskbotLOG -# sudo chown -R `whoami`:www-data $AskbotSITE -# sudo chown -R `whoami`:www-data $AskbotLOG -# chmod -R g+w $AskbotSITE/upfiles -# chmod -R g+w $AskbotLOG - - - Edit the settings files: - - cd $AskbotPROJECT - cp settings_local.py.dist settings_local.py - vi settings_local.py settings.py - - Pay attention to the following settings: - - DATABASE_ENGINE = 'mysql' - DATABASE_NAME = 'AskbotDATABASE_NAME' - DATABASE_USER = 'AskbotDATABASE_NAME' - DATABASE_PASSWORD = 'AskbotDATABASE_PASSWORD' - - EMAIL_HOST='smtp.webfaction.com' - EMAIL_HOST_USER='MAILBOX_USERNAME' - EMAIL_HOST_PASSWORD='MAILBOX_PASSWORD' - EMAIL_PORT='25' - DEFAULT_FROM_EMAIL = 'MAILBOX_USERNAME@DOMAIN.com' - SERVER_EMAIL = 'MAILBOX_USERNAME@DOMAIN.com' - # The following setting might not be necessary, it's used in Pinax tho - CONTACT_EMAIL = "MAILBOX_USERNAME@DOMAIN.com" - - APP_URL = 'http://DOMAIN.com' #used by email notif system and RSS - - [Later on, the install instructions should talk about] - SERVE_MEDIA = False # [Not present, not ready yet] - - Create a directory for logs: - - cd $AskbotPROJECT - mkdir log - - Modify mail cron scripts "cron/send_email_alerts" as follows: - [Pinax has cron/emit_notices.sh, cron/retry_deferred.sh, - cron/send_mail.sh, are these also necessary?] - - #!/bin/sh - - WORKON_HOME=~/envs/askbot - PROJECT_ROOT=~/webapps/askbot_server/projects/myaskbot/ - - # activate virtual environment - . $WORKON_HOME/bin/activate - - cd $PROJECT_ROOT - python manage.py send_email_alerts >> $PROJECT_ROOT/log/cron_mail.log 2>&1 - - Use command "crontab -e" to add this script to your cron file, to run twice a day:: - - 1 0,12 * * * ~/webapps/askbot_server/projects/myaskbot/cron/send_email_alerts - - [Configure sphinx] - - Create the database tables, indices, and so forth: - - python manage.py syncdb - - [Ignore the following static media steps, I haven't tried them] - Build media directory links within the project and create symbolic - links on the static media server. - python manage.py build_media -all - mkdir ~/webapps/Askbot_STATIC/myaskbot - ln -sd ~/webapps/askbot_server/projects/myaskbot/site_media ~/webapps/Askbot_STATIC/myaskbot/site_media - - - Set up the badges: - - 1. You should run the SQL commands in: - - sql_scripts/badges.sql - - 2. Edit paths in the file `cron/multi_award_badges`. (This - file doesn't yet exist in the git repositories, so just - copy `cron/send_email_alerts` and make sure the command - `multi_award_badges` is executed.) - - 3. Run `cron/multi_award_badges` to make sure it works okay. - - 4. Use `crontab -e` to call `cron/multi_award_badges` maybe - four times an hour. - - 4,19,34,49 * * * * ~/webapps/askbot_server/projects/myaskbot/cron/multi_award_badges - - 5. Repeat steps 1-4 for `cron/once_award_badges`. - - -Configure Apache2 ----------------- - - Edit ~/webapps/askbot_server/apache2/conf/httpd.conf as follows:: - - ServerAdmin "MAILBOX_USERNAME@DOMAIN.com" - ServerRoot "/home/USERNAME/webapps/askbot_server/apache2" - ServerName DOMAIN.com - - LoadModule dir_module modules/mod_dir.so - LoadModule env_module modules/mod_env.so - #LoadModule setenvif_module modules/mod_setenvif.so - LoadModule log_config_module modules/mod_log_config.so - LoadModule mime_module modules/mod_mime.so - LoadModule rewrite_module modules/mod_rewrite.so - LoadModule wsgi_module modules/mod_wsgi.so - - KeepAlive Off - Listen PORT - LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined - CustomLog /home/USERNAME/logs/user/access_askbot_server_log combined - ErrorLog /home/USERNAME/logs/user/error_askbot_server_log - ServerLimit 2 - - #SetEnvIf X-Forwarded-SSL on HTTPS=1 - - WSGIPythonPath /home/USERNAME/envs/askbot/lib/python2.5/site-packages/ - WSGIScriptAlias / /home/USERNAME/webapps/askbot_server/projects/myaskbot/django.wsgi - - LoadModule alias_module modules/mod_alias.so - WSGIDaemonProcess askbotWSGI user=USERNAME group=USERNAME threads=25 python-path=/home/USERNAME/envs/askbot/lib/python2.5/site-packages - WSGIProcessGroup askbotWSGI - - NameVirtualHost 127.0.0.1:PORT - - #ErrorLog "logs/myaskbot_2009_05_06.log" - SetHandler none - #Alias /site_media /home/USERNAME/webapps/static/myaskbot/site_media - - #force all content to be served as static files - #otherwise django will be crunching images through itself wasting time - Alias /content/ /home/USERNAME/webapps/askbot_server/projects/myaskbot/templates/content/ - Alias /forum/admin/media/ /home/turian/envs/askbot/lib/python2.5/site-packages/django/contrib/admin/media/ - #AliasMatch /([^/]*\.css) /home/USERNAME/webapps/askbot_server/projects/myaskbot/templates/content/style/$1 - - # Order deny,allow - # Allow from all - - - If you want virtual hosts of the admin interface under HTTPS, please - look at Askbot's install file. - - Edit ~/webapps/askbot_server/projects/myaskbot/deploy/django.wsgi as follows:: - - import os - import sys - - # redirect sys.stdout to sys.stderr for bad libraries like geopy that uses - # print statements for optional import exceptions. - sys.stdout = sys.stderr - - from os.path import abspath, dirname, join - from site import addsitedir - - # add the virtual environment site-packages to the path - from site import addsitedir - addsitedir('/home/USERNAME/envs/askbot/lib/python2.5/site-packages') - - sys.path.insert(0, abspath(join(dirname(__file__), "../"))) - sys.path.append(abspath(dirname(__file__))) - - from django.conf import settings - os.environ["DJANGO_SETTINGS_MODULE"] = "myaskbot.settings" - - #print sys.path - - from django.core.handlers.wsgi import WSGIHandler - application = WSGIHandler() - -And then you're up and running with: - - ~/webapps/askbot_server/apache2/bin/stop - ~/webapps/askbot_server/apache2/bin/start - -You should log in to the admin interface (http://DOMAIN.com/admin/), -and go to "Sites > Sites", and change the domain name that is used in -all emails. diff --git a/forum/documentation/ROADMAP.rst b/forum/documentation/ROADMAP.rst deleted file mode 100644 index c79e0ae4..00000000 --- a/forum/documentation/ROADMAP.rst +++ /dev/null @@ -1,88 +0,0 @@ -Intro -========= -ROADMAP aims to streamline activities of the Askbot open source project and -to minimize ad-hoc approaches of "big-picture" level. - -Aksbot is a Question and Asnwer system for the normal people! - -Let's discuss stuff that goes into this file on -http://groups.google.com/group/askbot - -Bacic principles of the project -================================== -Here they are: - -* our rule #1 is that all developers have commit right to the project - repository, but they must follow this ROADMAP and TODO - - to keep up with our own sanity. -* we welcome contributions by other people and show tolerance - and patience - especially to the new team members. -* when users who might not be tech-savvy ask questions - - we try to answer to the point and using their language - (i.e. not programmer jargon:) -* we favor plain and minimalistic style of programming, but pay - attention to detail - especially details of user experience. - -We try do develop using the following workflow: - -* specify problem that we try to solve -* create requirements that will guarantee a solution, once met -* dream up some implementation ideas (maybe even some sketches on the paper) -* discuss and decide on the best one -* write and test code - -The process doesn't have to be this formal all the time, but trying to stick -to some subset of this almost always helps! -Especially it helps to iron out disagreements between -individual programmers (which if you are one - you know are qute common -- and they don't all end well :). - -Ad-hoc programming - i.e. simply go and add code - is not really encouraged. -This works fine in the one person team or when the team consists of -best friends, but is almost sure to fail in a heterogenous group. - -Architecture and Requirements -===================================== -Obviously Django and Python are pre-made choices - so this -is not going to change any time soon. At this point all of -the client side Javascript is written using jQuery library. - -Our basic principle is that Askbot should be a mashable Q&A component. -Askbot is an application written in Python/Django. So it should be -distributable as a Django App alone or as a whole site (by option). - -If we develop sub-systems that can be used in the broader scope - -we package that thing as a separate django application (login system is one example). - -We will start using Google Closure library soon! - -Sub-systems ------------------ -* authentication system -* Q&A system -* admin interface -* full text search -* skins (directory forum/skins) - -Authentication system -------------------------- -Authentication system will be a separate django application - -Here is the discussion thread: -* http://groups.google.com/group/askbot/browse_thread/thread/1916dfcf666dd56c - -Most of the requirements are listed in the first message - -Skins ------------ -Skins eventually must be upgrade-stable - that is people who created custom -skins should not need to change anything if something changes in the code - -Admin interface ------------------------ -* extend forum/settings.py to list default settings of various groups -* create Registry database table the will store setting values -* leave only essential settings that go to the main django settings.py -Create key-value storage -* should some settings be accessible to admins and some to staff??? - for example-secret keys probably should not be shared with staff members diff --git a/forum/documentation/TODO.rst b/forum/documentation/TODO.rst deleted file mode 100644 index b89013b0..00000000 --- a/forum/documentation/TODO.rst +++ /dev/null @@ -1,70 +0,0 @@ -note: there is also WISH_LIST. Here is only stuff that will be done soon. - -Site looks -=========== -* make links within posts underlined -* add happy talk to tags and people view - -Code Cleanups -============== -* remove usage of EXTERNAL_LEGACY_LOGIN -* clean up forum_modules: - * keep this directory for dependency modules that can be shared - by multiple apps, - * but move other things that are not shared - inside forum app directory - * one-by one convert "auto-discovery" modules into - regular explicit python imports -* python2.4 incompatibilities - * datatime.datetime.strptime - -Search -======== -* make full text search work in postgres -* walkthrough sphinx search setup again -* text query lost when visiting question, it just doesn't print in Q view -* make username and tags persist in corresponding search windows - -Bugs -====== -* fix skin resolution bug in javascript - currently have to - copy skin media to customize - -Refactoring -============= -nothing right now - -Skins -======= -* organize templates and document them so that - skins could be more easily created by others - who are savvy enough -* identify and maybe create snippet-type templates - and put them into a separate directory - for example: - * gravatar (currently a string in - forum/templatetags/extra_tags.py - try inclusion template - and see if it slows things down a lot) - * question body - * answer body - * datetime widget??? -* there is a separator line between posts - but it shows either before the post or after - it is nice that separator is lightweight - - based on css alone - but we need to fix it so that - it shows only between the posts as a joining item - -Features -=========== -* new login system, please see - http://groups.google.com/group/askbot/browse_thread/thread/1916dfcf666dd56c - on a separate branch multi-auth-app, then merge -* forum admin interface, some badge configuration - -Development environment -========================== -* set up environment for closure development - -Project website -==================== -* Adopt Jekyll for project site and transition from Dango diff --git a/forum/documentation/UPGRADE b/forum/documentation/UPGRADE deleted file mode 100644 index 538b75a0..00000000 --- a/forum/documentation/UPGRADE +++ /dev/null @@ -1,24 +0,0 @@ -if you are upgrading this software, then - -* first download the newer version and write it over the old one. - -for the database migrations you will need to use django package called "south" - -Install it (if you don't have it yet) with: - - easy_install South - -* 'south' must already be in the list of INSTALLED_APPS - otherwise you must have downloaded wrong version of Askbot - -if you are using south the very first time, then type: - - python manage.py migrate forum 0001_initial --fake - -otherwise skip above step. - -Finally run - - python manage.py migrate forum --auto - -then all relevant schema and data migrations will be applied diff --git a/forum/documentation/WISH_LIST b/forum/documentation/WISH_LIST deleted file mode 100644 index 2b53662c..00000000 --- a/forum/documentation/WISH_LIST +++ /dev/null @@ -1,55 +0,0 @@ -* smarter debug mode -* The wonder bar (integrated the search / ask functionality) -* The authentication system ??? -* allow multiple logins to the same account -* allow multiple logins to the same account -* more advanced templating/skinning system -* per-tag email subscriptions -* view for personalized news on the site -* a little flag popping when there are news -* drill-down mode for navigation by tags -* improved admin console -* sort out mess with profile - currently we patch django User - -* Some functionality should be moved out of the forums app, in the case -that the forum app is restricted only to authenticated users: - - (r'^%s/$' % _('signin/'), 'django_authopenid.views.signin'), - url(r'^%s$' % _('about/'), app.about, name='about'), - url(r'^%s$' % _('faq/'), app.faq, name='faq'), - url(r'^%s$' % _('privacy/'), app.privacy, name='privacy'), - url(r'^%s$' % _('logout/'), app.logout, name='logout'), - url(r'^%s$' % _('feedback/'), app.feedback, name='feedback'), - (r'^%sfb/' % _('account/'), include('fbconnect.urls')), - (r'^%s' % _('account/'), include('django_authopenid.urls')), - -Copied from old todo list: - -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 --> askbot - without breaking things and keeping name of the project root - named the same way - askbot - -==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/forum/documentation/askbot-requirements.txt b/forum/documentation/askbot-requirements.txt deleted file mode 100644 index 66a37fbe..00000000 --- a/forum/documentation/askbot-requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -django>=1.1 # Note: email subscription sender job requires Django 1.1, everything else works with 1.0 -#mysql-python # Can't use with pip, see http://groups.google.com/group/python-virtualenv/msg/ea988085951c92b3 -python-openid -html5lib -markdown2 -git+git://github.com/robhudson/django-debug-toolbar.git -git+git://github.com/dcramer/django-sphinx.git -svn+http://recaptcha-django.googlecode.com/svn/trunk/ -svn+http://recaptcha.googlecode.com/svn/trunk/recaptcha-plugins/python/ diff --git a/forum/documentation/scratch b/forum/documentation/scratch deleted file mode 100644 index 948055fa..00000000 --- a/forum/documentation/scratch +++ /dev/null @@ -1,12 +0,0 @@ -Error upon restarting Apache: - -Name duplicates previous WSGI daemon definition - -different keys - empty space counts for translation keys -{% blocktrans %}page number {{num}} {% endblocktrans %} -{% blocktrans %}page number {{num}}{% endblocktrans %} - -for admin interface downloaded two packages: -django-keyedcache -django-livesettings -from http://bitbucket.org/bkroeze/ diff --git a/forum/feed.py b/forum/feed.py index e4b929e9..3d3a3241 100644 --- a/forum/feed.py +++ b/forum/feed.py @@ -1,7 +1,7 @@ #!/usr/bin/env python #encoding:utf-8 #------------------------------------------------------------------------------- -# Name: Syndication feed class for subsribtion +# Name: Syndication feed class for subscription # Purpose: # # Author: Mike @@ -13,10 +13,10 @@ from django.contrib.syndication.feeds import Feed, FeedDoesNotExist from django.utils.translation import ugettext as _ from models import Question -from django.conf import settings +from forum.conf import settings class RssLastestQuestionsFeed(Feed): - title = settings.APP_TITLE + _(' - ')+ _('latest questions') - link = settings.APP_URL #+ '/' + _('question/') + title = settings.ASKBOT_FORUM_TITLE + _(' - ')+ _('latest questions') + link = settings.APP_URL description = settings.APP_DESCRIPTION #ttl = 10 copyright = settings.APP_COPYRIGHT diff --git a/forum/forms.py b/forum/forms.py index e9781dc9..52cf00cc 100644 --- a/forum/forms.py +++ b/forum/forms.py @@ -11,6 +11,7 @@ from django.contrib.contenttypes.models import ContentType from forum.utils.forms import NextUrlField, UserNameField, SetPasswordForm from recaptcha_django import ReCaptchaField from django.conf import settings +from forum.conf import settings as forum_settings import logging @@ -65,7 +66,7 @@ class TagNamesField(forms.CharField): tag_strings = split_re.split(data) out_tag_list = [] tag_count = len(tag_strings) - if tag_count > const.MAX_TAGS_PER_POST: + if tag_count > forum_settings.MAX_TAGS_PER_POST: msg = ungettext( 'please use %(tag_count)d tag or less',#odd but have to use to pluralize 'please use %(tag_count)d tags or less', @@ -73,7 +74,7 @@ class TagNamesField(forms.CharField): raise forms.ValidationError(msg) for tag in tag_strings: tag_length = len(tag) - if tag_length > const.MAX_TAG_LENGTH: + if tag_length > forum_settings.MAX_TAG_LENGTH: #singular form is odd in english, but required for pluralization #in other languages msg = ungettext('each tag must be shorter than %(max_chars)d character',#odd but added for completeness @@ -97,7 +98,7 @@ class WikiField(forms.BooleanField): 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 + return value and forum_settings.WIKI_ON class EmailNotifyField(forms.BooleanField): def __init__(self, *args, **kwargs): @@ -231,7 +232,7 @@ class AnswerForm(forms.Form): 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: + if question.wiki and forum_settings.WIKI_ON: self.fields['wiki'].initial = True if user.is_authenticated(): if user in question.followed_by.all(): diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py index 42a6b3b3..2691a217 100644 --- a/forum/management/commands/send_email_alerts.py +++ b/forum/management/commands/send_email_alerts.py @@ -1,13 +1,14 @@ 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 forum.models import User, Question, Answer, Tag, QuestionRevision +from forum.models import AnswerRevision, Activity, EmailFeedSetting 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 +from forum.conf import settings as forum_settings import logging from forum.utils.odict import OrderedDict from django.contrib.contenttypes.models import ContentType @@ -18,7 +19,7 @@ def extend_question_list(src, dst, limit=False): or None dst - is an ordered dictionary """ - if limit and len(dst.keys()) >= const.MAX_ALERTS_PER_EMAIL: + if limit and len(dst.keys()) >= forum_settings.MAX_ALERTS_PER_EMAIL: return if src is None:#is not QuerySet return #will not do anything if subscription of this type is not used @@ -110,16 +111,16 @@ class Command(NoArgsCommand): q_ask_B = Q_set_B.filter(author=user) q_ask_B.cutoff_time = cutoff_time elif feed.feed_type == 'q_ans': - q_ans_A = Q_set_A.filter(answers__author=user)[:const.MAX_ALERTS_PER_EMAIL] + q_ans_A = Q_set_A.filter(answers__author=user)[:forum_settings.MAX_ALERTS_PER_EMAIL] q_ans_A.cutoff_time = cutoff_time - q_ans_B = Q_set_B.filter(answers__author=user)[:const.MAX_ALERTS_PER_EMAIL] + q_ans_B = Q_set_B.filter(answers__author=user)[:forum_settings.MAX_ALERTS_PER_EMAIL] q_ans_B.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_A = Q_set_A.exclude( tags__in=ignored_tags )[:const.MAX_ALERTS_PER_EMAIL] - q_all_B = Q_set_B.exclude( tags__in=ignored_tags )[:const.MAX_ALERTS_PER_EMAIL] + q_all_A = Q_set_A.exclude( tags__in=ignored_tags )[:forum_settings.MAX_ALERTS_PER_EMAIL] + q_all_B = Q_set_B.exclude( tags__in=ignored_tags )[:forum_settings.MAX_ALERTS_PER_EMAIL] else: selected_tags = Tag.objects.filter(user_selections__reason='good', \ user_selections__user=user) @@ -232,7 +233,7 @@ class Command(NoArgsCommand): else: num_q += 1 if num_q > 0: - url_prefix = settings.APP_URL + url_prefix = forum_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', @@ -246,7 +247,7 @@ class Command(NoArgsCommand): act_list = [] if meta_data['skip']: continue - if items_added >= const.MAX_ALERTS_PER_EMAIL: + if items_added >= forum_settings.MAX_ALERTS_PER_EMAIL: items_unreported = num_q - items_added #may be inaccurate actually, but it's ok else: @@ -261,7 +262,7 @@ class Command(NoArgsCommand): % (url_prefix + q.get_absolute_url(), q.title, act_token) text += '' text += '

' - #if len(q_list.keys()) >= const.MAX_ALERTS_PER_EMAIL: + #if len(q_list.keys()) >= forum_settings.MAX_ALERTS_PER_EMAIL: # text += _('There may be more questions updated since ' # 'you have logged in last time as this list is ' # 'abridged for your convinience. Please visit ' diff --git a/forum/middleware/anon_user.py b/forum/middleware/anon_user.py index 866734da..e0e4eb51 100644 --- a/forum/middleware/anon_user.py +++ b/forum/middleware/anon_user.py @@ -2,7 +2,6 @@ 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 @@ -29,7 +28,8 @@ class ConnectToSessionMessagesMiddleware(object): request.user.get_and_delete_messages = request.user.message_set.get_and_delete #also set the first greeting one time per session only + from forum.conf import settings if 'greeting_set' not in request.session: request.session['greeting_set'] = True - msg = _('First time here? Check out the FAQ!') % reverse('faq') + msg = _(const.GREETING_FOR_ANONYMOUS_USER) % settings.GREETING_URL request.user.message_set.create(message=msg) diff --git a/forum/models/__init__.py b/forum/models/__init__.py index 07d34402..5140f9e1 100644 --- a/forum/models/__init__.py +++ b/forum/models/__init__.py @@ -408,7 +408,7 @@ def record_user_full_updated(instance, **kwargs): 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 + from django.conf import settings if settings.EMAIL_VALIDATION == 'on':#add user to the record for aq in aq_list: aq.author = user diff --git a/forum/models/conf/README b/forum/models/conf/README new file mode 100644 index 00000000..4dd62329 --- /dev/null +++ b/forum/models/conf/README @@ -0,0 +1,5 @@ +this directory contains +forum site configurations for livesettings + +they need to be imported in models so made this a part of +models module diff --git a/forum/models/conf/__init__.py b/forum/models/conf/__init__.py new file mode 100644 index 00000000..f30617a1 --- /dev/null +++ b/forum/models/conf/__init__.py @@ -0,0 +1,62 @@ +import os +from livesettings import ConfigurationGroup, IntegerValue, config_register, SortedDotDict +from forum import models + +INSTALLED_APPS = ['forum'] + +MIDDLEWARE_CLASSES = [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'forum.middleware.anon_user.ConnectToSessionMessagesMiddleware', + 'forum.middleware.pagesize.QuestionsPageSizeMiddleware', + 'forum.middleware.cancel.CancelActionMiddleware', + 'django.middleware.transaction.TransactionMiddleware', +] + +TEMPLATE_LOADERS = [ + 'django.template.loaders.filesystem.load_template_source', + 'django.template.loaders.app_directories.load_template_source', + 'forum.modules.module_templates_loader', + 'forum.skins.load_template_source', +] + +TEMPLATE_CONTEXT_PROCESSORS = [ + 'django.core.context_processors.request', + 'forum.context.application_settings', + 'forum.user_messages.context_processors.user_messages', + 'django.core.context_processors.auth', +] + +TEMPLATE_DIRS = [ + os.path.join(os.path.dirname(__file__),'skins').replace('\\','/'), +] + +def setup_django_settings(settings): + + if (hasattr(settings, 'DEBUG') and getattr(settings, 'DEBUG')): + try: + import debug_toolbar + INSTALLED_APPS.append('debug_toolbar') + MIDDLEWARE_CLASSES.append('debug_toolbar.middleware.DebugToolbarMiddleware') + except: + pass + + + settings.INSTALLED_APPS = set(settings.INSTALLED_APPS) | set(INSTALLED_APPS) + settings.MIDDLEWARE_CLASSES = set(settings.MIDDLEWARE_CLASSES) | set(MIDDLEWARE_CLASSES) + settings.TEMPLATE_LOADERS = set(settings.TEMPLATE_LOADERS) | set(TEMPLATE_LOADERS) + settings.TEMPLATE_CONTEXT_PROCESSORS = set(settings.TEMPLATE_CONTEXT_PROCESSORS) | set(TEMPLATE_CONTEXT_PROCESSORS) + settings.TEMPLATE_DIRS = set(settings.TEMPLATE_DIRS) | set(TEMPLATE_DIRS) + +#import these to compile code and install values +import forum.conf.minimum_reputation +import forum.conf.vote_rules +import forum.conf.reputation_changes +import forum.conf.email +import forum.conf.forum_data_rules +import forum.conf.flatpages +import forum.conf.external_keys + +#import main settings object +from forum.conf.settings_wrapper import settings diff --git a/forum/models/conf/email.py b/forum/models/conf/email.py new file mode 100644 index 00000000..ca66d4ae --- /dev/null +++ b/forum/models/conf/email.py @@ -0,0 +1,20 @@ +""" +Email related settings +""" +from forum.conf.settings_wrapper import settings +from livesettings import ConfigurationGroup, IntegerValue +from django.utils.translation import ugettext as _ + +EMAIL = ConfigurationGroup( + 'EMAIL', + _('Email and email alert settings'), + ) + +settings.register( + IntegerValue( + EMAIL, + 'MAX_ALERTS_PER_EMAIL', + default=7, + description=_('Maximum number of news entries in an email alert') + ) +) diff --git a/forum/models/conf/external_keys.py b/forum/models/conf/external_keys.py new file mode 100644 index 00000000..5d0d47e4 --- /dev/null +++ b/forum/models/conf/external_keys.py @@ -0,0 +1,88 @@ +""" +External service key settings +""" +from forum.conf.settings_wrapper import settings +from livesettings import ConfigurationGroup, IntegerValue +from django.utils.translation import ugettext as _ + +EXTERNAL_KEYS = ConfigurationGroup( + 'EXTERNAL_KEYS', + _('Keys to connect the site with external services like Facebook, etc.') + ) + +settings.register( + StringValue( + EXTERNAL_KEYS, + 'GOOGLE_SITEMAP_CODE', + description=_('Google site verification key'), + help_text=_( + 'This key helps google index your site ' + 'please obtain is at ' + '' + 'google webmasters tools site' + ) % {'google_webmasters_tools_url': + 'https://www.google.com/webmasters/tools/home?hl=' \ + + django_settings.LANGUAGE_CODE} + ) +) + +settings.register( + StringValue( + EXTERNAL_KEYS, + 'GOOGLE_ANALYTICS_KEY', + description=_('Google Analytics key'), + help_text=_( + 'Obtain is at recaptcha.net' + ) + ) +) + +settings.register( + StringValue( + EXTERNAL_KEYS, + 'RECAPTCHA_PUBLIC_KEY', + description=_('Recaptcha public key') + ' - does not work yet') + ) +) + +settings.register( + StringValue( + EXTERNAL_KEYS, + 'FB_API_KEY', + description=_('Facebook public API key') + ' - does not work yet', + help_text=_( + 'Facebook API key and Facebook secret ' + 'allow to use Facebook Connect login method ' + 'at your site. Please obtain these keys ' + 'at ' + 'validate HTML' + ) % {'url':reverse('about')} + ) +) + +settings.register( + LongStringValue( + FLATPAGES, + 'FORUM_PRIVACY', + description=_('Text the Q&A forum Privacy Policy (html format)'), + help_text=\ + _( + 'Save, then ' + 'validate HTML' + ) % {'url':reverse('privacy')} + ) +) diff --git a/forum/models/conf/forum_data_rules.py b/forum/models/conf/forum_data_rules.py new file mode 100644 index 00000000..fcce0987 --- /dev/null +++ b/forum/models/conf/forum_data_rules.py @@ -0,0 +1,53 @@ +""" +Settings for forum data display and entry +""" +from forum.conf.settings_wrapper import settings +from livesettings import ConfigurationGroup, BooleanValue, IntegerValue +from livesettings import StringValue +from django.utils.translation import ugettext as _ +from forum import const + +FORUM_DATA_RULES = ConfigurationGroup( + 'FORUM_DATA_RULES', + _('Settings for forum data entry and display') + ) + +settings.register( + BooleanValue( + FORUM_DATA_RULES, + 'WIKI_ON', + default=True, + description=_('Enable/disable community wiki feature') + ) +) + +settings.register( + IntegerValue( + FORUM_DATA_RULES, + 'MAX_TAG_LENGTH', + default=20, + description=_('Maximum length of tag (number of characters)') + ) +) + +#todo: looks like there is a bug in livesettings +#that does not allow Integer values with defaults and choices +settings.register( + StringValue( + FORUM_DATA_RULES, + 'DEFAULT_QUESTIONS_PAGE_SIZE', + choices=const.PAGE_SIZE_CHOICES, + default='30', + description=_('Number of questions to list by default') + ) +) + +settings.register( + StringValue( + FORUM_DATA_RULES, + 'UNANSWERED_QUESTION_MEANING', + choices=const.UNANSWERED_QUESTION_MEANING_CHOICES, + default='NO_ACCEPTED_ANSWERS', + description=_('What should "unanswered question" mean?') + ) +) diff --git a/forum/models/conf/minimum_reputation.py b/forum/models/conf/minimum_reputation.py new file mode 100644 index 00000000..a83d94fd --- /dev/null +++ b/forum/models/conf/minimum_reputation.py @@ -0,0 +1,148 @@ +""" +Settings for minimum reputation required for +a variety of actions on the askbot forum +""" +from forum.conf.settings_wrapper import settings +from livesettings import ConfigurationGroup, IntegerValue +from django.utils.translation import ugettext as _ + +MIN_REP = ConfigurationGroup( + 'MIN_REP', + _('Minimum reputation required to perform actions'), + ordering=0 + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_VOTE_UP', + default=15, + description=_('Upvote') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_VOTE_DOWN', + default=100, + description=_('Downvote') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_FLAG_OFFENSIVE', + default=15, + description=_('Flag offensive') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_LEAVE_COMMENTS', + default=50, + description=_('Leave comments') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_DELETE_OTHERS_COMMENTS', + default=2000, + description=_('Delete comments posted by others') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_UPLOAD_FILES', + default=60, + description=_('Upload files') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_CLOSE_OWN_QUESTIONS', + default=250, + description=_('Close own questions'), + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_RETAG_OTHERS_QUESTIONS', + default=500, + description=_('Retag questions posted by other people') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_REOPEN_OWN_QUESTIONS', + default=500, + description=_('Reopen own questions') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_EDIT_WIKI', + default=750, + description=_('Edit community wiki posts') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_EDIT_OTHERS_POSTS', + default=2000, + description=_('Edit posts authored by other people') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_VIEW_OFFENSIVE_FLAGS', + default=2000, + description=_('View offensive flags') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_DISABLE_URL_NOFOLLOW', + default=2000, + description=_('Disable nofollow directive on links') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_CLOSE_OTHERS_QUESTIONS', + default=2000, + description=_('Close questions asked by others') + ) + ) + +settings.register( + IntegerValue( + MIN_REP, + 'MIN_REP_TO_LOCK_POSTS', + default=4000, + description=_('Lock posts') + ) + ) diff --git a/forum/models/conf/reputation_changes.py b/forum/models/conf/reputation_changes.py new file mode 100644 index 00000000..cef177f5 --- /dev/null +++ b/forum/models/conf/reputation_changes.py @@ -0,0 +1,149 @@ +""" +Settings for reputation changes that apply to +user in response to various actions by the same +users or others +""" +from forum.conf.settings_wrapper import settings +from livesettings import ConfigurationGroup, IntegerValue +from django.utils.translation import ugettext as _ + +REP_CHANGES = ConfigurationGroup( + 'REP_CHANGES', + _('Reputaion loss and gain rules'), + ordering=2 + ) + +settings.register( + IntegerValue( + REP_CHANGES, + 'MAX_REP_GAIN_PER_USER_PER_DAY', + default=200, + description=_('Maximum daily reputation gain per user') + ) +) + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_GAIN_FOR_RECEIVING_UPVOTE', + default=10, + description=_('Gain for receiving an upvote') + ) +) + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_GAIN_FOR_RECEIVING_ANSWER_ACCEPTANCE', + default=15, + description=_('Gain for the author of accepted answer') + ) +) + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_GAIN_FOR_ACCEPTING_ANSWER', + default=2, + description=_('Gain for accepting best answer') + ) +) + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_GAIN_FOR_RECEIVING_DOWNVOTE_CANCELATION', + default=2, + description=_('Gain for post owner on canceled downvote') + ) +) + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_GAIN_FOR_CANCELING_DOWNVOTE', + default=1, + description=_('Gain for voter on canceling downvote') + ) +) +#'gain_by_canceling_downvote', + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_LOSS_FOR_CANCELING_ANSWER_ACCEPTANCE', + default=-2, + description=_('Loss for voter for canceling of answer acceptance') + ) +) +#'lose_by_canceling_accepted_answer', + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_LOSS_FOR_RECEIVING_CANCELATION_OF_ANSWER_ACCEPTANCE', + default=-5, + description=_('Loss for author whose answer was "un-accepted"') + ) +) +#'lose_by_accepted_answer_cancled', + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_LOSS_FOR_DOWNVOTING', + default=-2, + description=_('Loss for giving a downvote') + ) +) +#'lose_by_downvoted', + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_LOSS_FOR_RECEIVING_FLAG', + default=-2, + description=_('Loss for owner of post that was flagged offensive') + ) +) +#'lose_by_flagged', + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_LOSS_FOR_RECEIVING_DOWNVOTE', + default=-1, + description=_('Loss for owner of post that was downvoted') + ) +) +#'lose_by_downvoting', + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_LOSS_FOR_RECEIVING_THREE_FLAGS_PER_REVISION', + default=-30, + description=_('Loss for owner of post that was flagged 3 times per same revision') + ) +) +#'lose_by_flagged_lastrevision_3_times', + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_LOSS_FOR_RECEIVING_FIVE_FLAGS_PER_REVISION', + default=-100, + description=_('Loss for owner of post that was flagged 5 times per same revision') + ) +) +#'lose_by_flagged_lastrevision_5_times', + +settings.register( + IntegerValue( + REP_CHANGES, + 'REP_LOSS_FOR_RECEIVING_UPVOTE_CANCELATION', + default=-10, + description=_('Loss for post owner when upvote is canceled') + ) +) +#'lose_by_upvote_canceled', diff --git a/forum/models/conf/settings_wrapper.py b/forum/models/conf/settings_wrapper.py new file mode 100644 index 00000000..abf49e5f --- /dev/null +++ b/forum/models/conf/settings_wrapper.py @@ -0,0 +1,59 @@ +""" +Definition of a Singleton wrapper class for livesettings +with interface similar to django.conf.settings +that is each setting has unique key and is accessible +via dotted lookup. + +for example to lookup value of setting BLAH you would do + +from forum.conf import settings + +settings.BLAH + +the value will be taken from livesettings database or cache +note that during compilation phase database is not accessible +for the most part, so actual values are reliably available only +at run time + +livesettings is a module developed for satchmo project +""" +from livesettings import SortedDotDict, config_register + +class ConfigSettings(object): + """A very simple Singleton wrapper for settings + a limitation is that all settings names using this class + must be distinct, even though they might belong + to different settings groups + """ + __instance = None + + def __init__(self): + """assigns SortedDotDict to self.__instance if not set""" + if ConfigSettings.__instance == None: + ConfigSettings.__instance = SortedDotDict() + self.__dict__['_ConfigSettings__instance'] = ConfigSettings.__instance + + def __getattr__(self, key): + """value lookup returns the actual value of setting + not the object - this way only very minimal modifications + will be required in code to convert an app + depending on django.conf.settings to livesettings + """ + return getattr(self.__instance, key).value + + def __setattr__(self, attr, value): + """ settings crutch is read-only in the program """ + raise Exception('ConfigSettings cannot be changed programmatically') + + def register(self, value): + """registers the setting + value must be a subclass of livesettings.Value + """ + key = value.key + if key in self.__instance: + raise Exception('setting %s is already registered' % key) + else: + self.__instance[key] = config_register(value) + +#settings instance to be used elsewhere in the project +settings = ConfigSettings() diff --git a/forum/models/conf/site_settings.py b/forum/models/conf/site_settings.py new file mode 100644 index 00000000..e3eb2023 --- /dev/null +++ b/forum/models/conf/site_settings.py @@ -0,0 +1,81 @@ +""" +Q&A website settings - title, desctiption, basic urls +keywords +""" +from forum.conf.settings_wrapper import settings +from livesettings import ConfigurationGroup, StringValue +from django.utils.translation import ugettext as _ +from django.conf import settings as django_settings +from forum import const +from django.core.urlresolvers import reverse + +QA_SITE_SETTINGS = ConfigurationGroup( + 'QA_SITE_SETTINGS', + _('Q&A forum website parameters and urls') + ) + +settings.register( + StringValue( + QA_SITE_SETTINGS, + 'APP_TITLE', + default=u'ASKBOT: Open Source Q&A Forum', + description=_('Site title for the Q&A forum') + ) +) + +settings.register( + StringValue( + QA_SITE_SETTINGS, + 'APP_KEYWORDS', + default=u'ASKBOT,CNPROG,forum,community', + description=_('Comma separated list of Q&A site keywords') + ) +) + +settings.register( + StringValue( + QA_SITE_SETTINGS, + 'APP_COPYRIGHT', + default='Copyright ASKBOT, 2010. Some rights reserved under creative commons license.', + description=_('Copyright message to show in the footer') + ) +) + +settings.register( + StringValue( + QA_SITE_SETTINGS, + 'APP_DESCRIPTION', + default='Open source question and answer forum written in Python and Django', + description=_('Site description for the search engines') + ) +) + +settings.register( + StringValue( + QA_SITE_SETTINGS, + 'APP_URL', + default='http://askbot.org', + description=_('Base URL for your Q&A forum, must start with http or https'), + ) +) + +settings.register( + StringValue( + QA_SITE_SETTINGS, + 'GREETING_URL', + default=reverse('faq'), + description=_('Link shown in the greeting message shown to the anonymous user'), + help_text=_('If you change this url from the default - ' + 'then you wil have to adjust translation of ' + 'the following string: ') + const.GREETING_FOR_ANONYMOUS_USER + ) +) + +settings.register( + StringValue( + QA_SITE_SETTINGS, + 'FEEBACK_SITE_URL' + description=_('Feedback site URL'), + help_text=_('If left empty, a simple internal feedback form will be used instead') + ) +) diff --git a/forum/models/conf/vote_rules.py b/forum/models/conf/vote_rules.py new file mode 100644 index 00000000..f249ef53 --- /dev/null +++ b/forum/models/conf/vote_rules.py @@ -0,0 +1,69 @@ +""" +Forum configuration settings detailing rules on votes +and offensive flags. + +For example number of times a person can vote each day, etc. +""" +from forum.conf.settings_wrapper import settings +from livesettings import ConfigurationGroup, IntegerValue +from django.utils.translation import ugettext as _ + +VOTE_RULES = ConfigurationGroup( + 'VOTE_RULES', + _('Limits applicable to votes and moderation flags'), + ordering=1, + ) + +settings.register( + IntegerValue( + VOTE_RULES, + 'MAX_VOTES_PER_USER_PER_DAY', + default=30, + description=_('Number of votes a user can cast per day') + ) +) + +settings.register( + IntegerValue( + VOTE_RULES, + 'MAX_FLAGS_PER_USER_PER_DAY', + default=5, + description=_('Maximum number of flags per user per day') + ) +) + +settings.register( + IntegerValue( + VOTE_RULES, + 'VOTES_LEFT_WARNING_THRESHOLD', + default=5, + description=_('Threshold for warning about remaining daily votes') + ) +) + +settings.register( + IntegerValue( + VOTE_RULES, + 'MAX_DAYS_TO_CANCEL_VOTE', + default=1, + description=_('Number of days to allow canceling votes') + ) +) + +settings.register( + IntegerValue( + VOTE_RULES, + 'MIN_FLAGS_TO_HIDE_POST', + default=3, + description=_('Number of flags required to automatically hide posts') + ) +) + +settings.register( + IntegerValue( + VOTE_RULES, + 'MIN_FLAGS_TO_DELETE_POST', + default=5, + description=_('Number of flags required to automatically delete posts') + ) +) diff --git a/forum/models/question.py b/forum/models/question.py index 4d3154b0..1d387ab7 100644 --- a/forum/models/question.py +++ b/forum/models/question.py @@ -96,16 +96,19 @@ class QuestionManager(models.Manager): params=['%' + search_query + '%'] ) + #have to import this at run time, otherwise there + #a circular import dependency... + from forum.conf import settings as forum_settings if scope_selector: if scope_selector == 'unanswered': - if const.UNANSWERED_MEANING == 'NO_ANSWERS': + if forum_settings.UNANSWERED_QUESTION_MEANING == 'NO_ANSWERS': qs = qs.filter(answer_count=0)#todo: expand for different meanings of this - elif const.UNANSWERED_MEANING == 'NO_ACCEPTED_ANSWERS': + elif forum_settings.UNANSWERED_QUESTION_MEANING == 'NO_ACCEPTED_ANSWERS': qs = qs.filter(answer_accepted=False) - elif const.UNANSWERED_MEANING == 'NO_UPVOTED_ANSWERS': + elif forum_settings.UNANSWERED_QUESTION_MEANING == 'NO_UPVOTED_ANSWERS': raise NotImplementedError() else: - raise Exception('UNANSWERED_MEANING setting is wrong') + raise Exception('UNANSWERED_QUESTION_MEANING setting is wrong') elif scope_selector == 'favorite': qs = qs.filter(favorited_by = request_user) @@ -513,7 +516,7 @@ class Question(Content, DeletableContent): 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() + url = forum_settings.APP_URL + self.get_absolute_url() retval = '%s:
\n' % (url,self.title) out = map(lambda x: '
  • ' + x + '
  • ',out) retval += '
      ' + '\n'.join(out) + '

    \n' diff --git a/forum/search/state_manager.py b/forum/search/state_manager.py index 6a38a7a2..ac3f7014 100644 --- a/forum/search/state_manager.py +++ b/forum/search/state_manager.py @@ -2,6 +2,7 @@ #that lives in the session and takes care of the state #persistece during the search session from forum import const +from forum.conf import settings import logging ACTIVE_COMMANDS = ( @@ -24,7 +25,7 @@ class SearchState(object): self.tags = None self.author = None self.sort = const.DEFAULT_POST_SORT_METHOD - self.page_size = const.DEFAULT_QUESTIONS_PAGE_SIZE + self.page_size = int(settings.DEFAULT_QUESTIONS_PAGE_SIZE) self.page = 1 self.logged_in = False logging.debug('new search state initialized') diff --git a/forum/settings.py b/forum/settings.py index 4147be8c..6e31634f 100644 --- a/forum/settings.py +++ b/forum/settings.py @@ -1,3 +1,4 @@ +#todo: this file is currently not in use import os from livesettings import ConfigurationGroup, IntegerValue, config_register diff --git a/forum/skins/default/templates/about.html b/forum/skins/default/templates/about.html index 686141b3..d091725b 100644 --- a/forum/skins/default/templates/about.html +++ b/forum/skins/default/templates/about.html @@ -12,25 +12,7 @@
    -

    Please customize file templates/about.html

    - -

    Here you can ask and answer questions, comment - and vote for the questions of others and their answers. Both questions and answers - can be revised and improved. Questions can be tagged with - the relevant keywords to simplify future access and organize the accumulated material. -

    - -

    This Q&A site is moderated by its members, hopefully - including yourself! - Moderation rights are gradually assigned to the site users based on the accumulated "reputation" - points. These points are added to the users account when others vote for his/her questions or answers. - These points (very) roughly reflect the level of trust of the community. -

    -

    No points are necessary to ask or answer the questions - so please - - join us! -

    -

    - If you would like to find out more about this site - please see frequently asked questions. -

    + {{settings.FORUM_ABOUT|safe}}
    {% endblock %} diff --git a/forum/skins/default/templates/base.html b/forum/skins/default/templates/base.html index a4e4ceed..94d3392d 100644 --- a/forum/skins/default/templates/base.html +++ b/forum/skins/default/templates/base.html @@ -9,8 +9,12 @@ {% block title %}{% endblock %} - {{ settings.APP_TITLE }} {% spaceless %} {% block meta %}{% endblock %} + {% block meta_description %} + + {% endblock %} {% endspaceless %} + {% if settings.GOOGLE_SITEMAP_CODE %} {% endif %} diff --git a/forum/skins/default/templates/base_content.html b/forum/skins/default/templates/base_content.html index 284007d8..7b49d9ba 100644 --- a/forum/skins/default/templates/base_content.html +++ b/forum/skins/default/templates/base_content.html @@ -6,6 +6,10 @@ {% block title %}{% endblock %} - {{ settings.APP_TITLE }} + + {% block meta_description %} + + {% endblock %} {% if settings.GOOGLE_SITEMAP_CODE %} {% endif %} diff --git a/forum/skins/default/templates/footer.html b/forum/skins/default/templates/footer.html index 9b7c5d98..a28a1980 100644 --- a/forum/skins/default/templates/footer.html +++ b/forum/skins/default/templates/footer.html @@ -23,7 +23,7 @@

    powered by ASKBOT - +
    {{settings.APP_COPYRIGHT}}

    -

    - {% trans "general message about privacy" %} -

    - -

    {% trans "Site Visitors" %}

    -

    - {% trans "what technical information is collected about visitors" %} -

    - -

    {% trans "Personal Information" %}

    -

    - {% trans "details on personal information policies" %} -

    - -

    {% trans "Other Services" %}

    -

    - {% trans "details on sharing data with third parties" %} -

    - -

    Cookies

    -

    - {% trans "cookie policy details" %} -

    -

    {% trans "Policy Changes" %}

    -

    {% trans "how privacy policies can be changed" %} -

    + {{settings.FORUM_PRIVACY|safe}}
    {% endblock %} diff --git a/forum/skins/default/templates/question.html b/forum/skins/default/templates/question.html index 48b6d719..84ef9978 100644 --- a/forum/skins/default/templates/question.html +++ b/forum/skins/default/templates/question.html @@ -7,9 +7,11 @@ {% load i18n %} {% load cache %} {% block title %}{% spaceless %}{{ question.get_question_title }}{% endspaceless %}{% endblock %} -{% block forejs %} +{% block meta_description %} - +{% endblock %} +{% block keywords %}{{question.tagname_meta_generator}}{% endblock %} +{% block forejs %} {% if not question.closed %} diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py index 86f2e9df..36de4ec5 100644 --- a/forum/templatetags/extra_tags.py +++ b/forum/templatetags/extra_tags.py @@ -13,6 +13,7 @@ from forum.models import Question, Answer, QuestionRevision, AnswerRevision from django.utils.translation import ugettext as _ from django.utils.translation import ungettext from django.conf import settings +from forum.conf import settings as forum_settings from django.template.defaulttags import url as default_url from django.template.defaultfilters import slugify from django.core.urlresolvers import reverse @@ -146,7 +147,7 @@ def post_contributor_info(post,contributor_type='original_author'): return { 'post':post, 'post_type':post_type, - 'wiki_on':settings.WIKI_ON, + 'wiki_on':forum_settings.WIKI_ON, 'contributor_type':contributor_type } @@ -395,7 +396,7 @@ class FullUrlNode(template.Node): self.default_node = default_node def render(self, context): - domain = settings.APP_URL + domain = forum_settings.APP_URL #protocol = getattr(settings, "PROTOCOL", "http") path = self.default_node.render(context) return "%s%s" % (domain, path) @@ -407,7 +408,7 @@ def fullurl(parser, token): @register.simple_tag def fullmedia(url): - domain = settings.APP_URL + domain = forum_settings.APP_URL #protocol = getattr(settings, "PROTOCOL", "http") path = media(url) return "%s%s" % (domain, path) diff --git a/settings.py b/settings.py index 97d80e97..98555997 100644 --- a/settings.py +++ b/settings.py @@ -89,9 +89,6 @@ INSTALLED_APPS = ( AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',) -if USE_FB_CONNECT: - INSTALLED_APPS += ('fbconnect',) - #this needs to go if 'USE_EXTERNAL_LEGACY_LOGIN' in locals() and USE_EXTERNAL_LEGACY_LOGIN: INSTALLED_APPS += (EXTERNAL_LEGACY_LOGIN_MODULE,) -- cgit v1.2.3-1-g7c22