This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
bistro/askbot/const/__init__.py

474 lines
15 KiB
Python

# encoding:utf-8
"""
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.
"""
from django.utils.translation import ugettext_lazy as _
import re
#todo: customize words
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')),
)
LONG_TIME = 60*60*24*30 #30 days is a lot of time
DATETIME_FORMAT = '%I:%M %p, %d %b %Y'
SHARE_NOTHING = 0
SHARE_MY_POSTS = 1
SHARE_EVERYTHING = 2
SOCIAL_SHARING_MODE_CHOICES = (
(SHARE_NOTHING, _('disable sharing')),
(SHARE_MY_POSTS, _('my posts')),
(SHARE_EVERYTHING, _('all posts'))
)
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'),
#for reputation type 10 Repute.comment field is required
(10, 'assigned_by_moderator'),
)
#do not translate keys
POST_SORT_METHODS = (
('age-desc', _('newest')),
('age-asc', _('oldest')),
('activity-desc', _('active')),
('activity-asc', _('inactive')),
('answers-desc', _('hottest')),
('answers-asc', _('coldest')),
('votes-desc', _('most voted')),
('votes-asc', _('least voted')),
('relevance-desc', _('relevance')),
)
POST_TYPES = ('answer', 'comment', 'question', 'tag_wiki', 'reject_reason')
SIMPLE_REPLY_SEPARATOR_TEMPLATE = '==== %s -=-=='
#values for SELF_NOTIFY_WHEN... settings use bits
NEVER = 'never'
FOR_FIRST_REVISION = 'first'
FOR_ANY_REVISION = 'any'
SELF_NOTIFY_EMAILED_POST_AUTHOR_WHEN_CHOICES = (
(NEVER, _('Never')),
(FOR_FIRST_REVISION, _('When new post is published')),
(FOR_ANY_REVISION, _('When post is published or revised')),
)
#need more options for web posts b/c user is looking at the page
#when posting. when posts are made by email - user is not looking
#at the site and therefore won't get any feedback unless an email is sent back
#todo: rename INITIAL -> FIRST and make values of type string
#FOR_INITIAL_REVISION_WHEN_APPROVED = 1
#FOR_ANY_REVISION_WHEN_APPROVED = 2
#FOR_INITIAL_REVISION_ALWAYS = 3
#FOR_ANY_REVISION_ALWAYS = 4
#SELF_NOTIFY_WEB_POST_AUTHOR_WHEN_CHOICES = (
# (NEVER, _('Never')),
# (
# FOR_INITIAL_REVISION_WHEN_APPROVED,
# _('When inital revision is approved by moderator')
# ),
# (
# FOR_ANY_REVISION_WHEN_APPROVED,
# _('When any revision is approved by moderator')
# ),
# (
# FOR_INITIAL_REVISION_ALWAYS,
# _('Any time when inital revision is published')
# ),
# (
# FOR_ANY_REVISION_ALWAYS,
# _('Any time when revision is published')
# )
#)
REPLY_SEPARATOR_TEMPLATE = '==== %(user_action)s %(instruction)s -=-=='
REPLY_WITH_COMMENT_TEMPLATE = _(
'Note: to reply with a comment, '
'please use <a href="mailto:%(addr)s?subject=%(subject)s">this link</a>'
)
REPLY_SEPARATOR_REGEX = re.compile(r'==== .* -=-==', re.MULTILINE|re.DOTALL)
ANSWER_SORT_METHODS = (
('latest' , _('latest first')),
('oldest', _('oldest first')),
('votes', _('most voted first')),
)
DEFAULT_ANSWER_SORT_METHOD = 'votes'
#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 = 'activity-desc'
#todo: customize words
POST_SCOPE_LIST = (
('all', _('all')),
('unanswered', _('unanswered')),
('followed', _('followed')),
)
DEFAULT_POST_SCOPE = 'all'
TAG_LIST_FORMAT_CHOICES = (
('list', _('list')),
('cloud', _('cloud')),
)
PAGE_SIZE_CHOICES = (('10', '10',), ('30', '30',), ('50', '50',),)
ANSWERS_PAGE_SIZE = 10
USER_POSTS_PAGE_SIZE = 6
QUESTIONS_PER_PAGE_USER_CHOICES = ((10, u'10'), (30, u'30'), (50, u'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
#IMPRTANT: tag related regexes must be portable between js and python
TAG_CHARS = r'\w+.#-'
TAG_FIRST_CHARS = r'\w'
TAG_FORBIDDEN_FIRST_CHARS = r'#'
TAG_REGEX_BARE = r'%s[%s]+' % (TAG_FIRST_CHARS, TAG_CHARS)
TAG_REGEX = r'^%s$' % TAG_REGEX_BARE
TAG_SPLIT_REGEX = r'[ ,]+'
TAG_SEP = ',' # has to be valid TAG_SPLIT_REGEX char and MUST NOT be in const.TAG_CHARS
#!!! see const.message_keys.TAG_WRONG_CHARS_MESSAGE
EMAIL_REGEX = re.compile(r'\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b', re.I)
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_EMAIL_UPDATE_SENT = 18
TYPE_ACTIVITY_MENTION = 19
TYPE_ACTIVITY_UNANSWERED_REMINDER_SENT = 20
TYPE_ACTIVITY_ACCEPT_ANSWER_REMINDER_SENT = 21
TYPE_ACTIVITY_CREATE_TAG_WIKI = 22
TYPE_ACTIVITY_UPDATE_TAG_WIKI = 23
TYPE_ACTIVITY_MODERATED_NEW_POST = 24
TYPE_ACTIVITY_MODERATED_POST_EDIT = 25
TYPE_ACTIVITY_CREATE_REJECT_REASON = 26
TYPE_ACTIVITY_UPDATE_REJECT_REASON = 27
TYPE_ACTIVITY_VALIDATION_EMAIL_SENT = 28
TYPE_ACTIVITY_POST_SHARED = 29
TYPE_ACTIVITY_ASK_TO_JOIN_GROUP = 30
#TYPE_ACTIVITY_EDIT_QUESTION = 17
#TYPE_ACTIVITY_EDIT_ANSWER = 18
#todo: rename this to TYPE_ACTIVITY_CHOICES
TYPE_ACTIVITY = (
(TYPE_ACTIVITY_ASK_QUESTION, _('asked a question')),
(TYPE_ACTIVITY_ANSWER, _('answered a question')),
(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 badge')),
(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_EMAIL_UPDATE_SENT, _('email update sent to user')),
(TYPE_ACTIVITY_POST_SHARED, _('a post was shared')),
(
TYPE_ACTIVITY_UNANSWERED_REMINDER_SENT,
_('reminder about unanswered questions sent'),
),
(
TYPE_ACTIVITY_ACCEPT_ANSWER_REMINDER_SENT,
_('reminder about accepting the best answer sent'),
),
(TYPE_ACTIVITY_MENTION, _('mentioned in the post')),
(
TYPE_ACTIVITY_CREATE_TAG_WIKI,
_('created tag description'),
),
(
TYPE_ACTIVITY_UPDATE_TAG_WIKI,
_('updated tag description')
),
(TYPE_ACTIVITY_MODERATED_NEW_POST, _('made a new post')),
(
TYPE_ACTIVITY_MODERATED_POST_EDIT,
_('made an edit')
),
(
TYPE_ACTIVITY_CREATE_REJECT_REASON,
_('created post reject reason'),
),
(
TYPE_ACTIVITY_UPDATE_REJECT_REASON,
_('updated post reject reason')
),
(
TYPE_ACTIVITY_VALIDATION_EMAIL_SENT,
'sent email address validation message'#don't translate, internal
),
)
#MENTION activity is added implicitly, unfortunately
RESPONSE_ACTIVITY_TYPES_FOR_INSTANT_NOTIFICATIONS = (
#TYPE_ACTIVITY_COMMENT_QUESTION,
#TYPE_ACTIVITY_COMMENT_ANSWER,
#TYPE_ACTIVITY_UPDATE_ANSWER,
#TYPE_ACTIVITY_UPDATE_QUESTION,
TYPE_ACTIVITY_ANSWER,
TYPE_ACTIVITY_ASK_QUESTION,
#TYPE_ACTIVITY_POST_SHARED
)
#the same as for instant notifications for now
#MENTION activity is added implicitly, unfortunately
RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY = (
TYPE_ACTIVITY_ANSWER,
TYPE_ACTIVITY_ASK_QUESTION,
TYPE_ACTIVITY_COMMENT_QUESTION,
TYPE_ACTIVITY_COMMENT_ANSWER,
TYPE_ACTIVITY_UPDATE_ANSWER,
TYPE_ACTIVITY_UPDATE_QUESTION,
TYPE_ACTIVITY_POST_SHARED,
# TYPE_ACTIVITY_PRIZE,
# TYPE_ACTIVITY_MARK_ANSWER,
# TYPE_ACTIVITY_VOTE_UP,
# TYPE_ACTIVITY_VOTE_DOWN,
# TYPE_ACTIVITY_CANCEL_VOTE,
# TYPE_ACTIVITY_DELETE_QUESTION,
# TYPE_ACTIVITY_DELETE_ANSWER,
# TYPE_ACTIVITY_MARK_OFFENSIVE,
# TYPE_ACTIVITY_FAVORITE,
)
RESPONSE_ACTIVITY_TYPE_MAP_FOR_TEMPLATES = {
#TYPE_ACTIVITY_COMMENT_QUESTION: 'question_comment',
#TYPE_ACTIVITY_COMMENT_ANSWER: 'answer_comment',
#TYPE_ACTIVITY_UPDATE_ANSWER: 'answer_update',
#TYPE_ACTIVITY_UPDATE_QUESTION: 'question_update',
TYPE_ACTIVITY_ANSWER: 'new_answer',
TYPE_ACTIVITY_ASK_QUESTION: 'new_question',
#TYPE_ACTIVITY_POST_SHARED: 'post_shared'
}
assert(
set(RESPONSE_ACTIVITY_TYPES_FOR_INSTANT_NOTIFICATIONS) \
== set(RESPONSE_ACTIVITY_TYPE_MAP_FOR_TEMPLATES.keys())
)
POST_STATUS = {
'closed': _('[closed]'),
'deleted': _('[deleted]'),
'default_version': _('initial version'),
'retagged': _('retagged'),
'private': _('[private]')
}
#choices used in email and display filters
INCLUDE_ALL = 0
EXCLUDE_IGNORED = 1
INCLUDE_INTERESTING = 2
INCLUDE_SUBSCRIBED = 3
TAG_DISPLAY_FILTER_STRATEGY_MINIMAL_CHOICES = (
(INCLUDE_ALL, _('show all tags')),
(EXCLUDE_IGNORED, _('exclude ignored tags')),
(INCLUDE_INTERESTING, _('only interesting tags'))
)
TAG_DISPLAY_FILTER_STRATEGY_CHOICES = \
TAG_DISPLAY_FILTER_STRATEGY_MINIMAL_CHOICES + \
((INCLUDE_SUBSCRIBED, _('only subscribed tags')),)
TAG_EMAIL_FILTER_SIMPLE_STRATEGY_CHOICES = (
(INCLUDE_ALL, _('email for all tags')),
(EXCLUDE_IGNORED, _('exclude ignored tags')),
(INCLUDE_INTERESTING, _('only interesting tags')),
)
TAG_EMAIL_FILTER_ADVANCED_STRATEGY_CHOICES = (
(INCLUDE_ALL, _('email for all tags')),
(EXCLUDE_IGNORED, _('exclude ignored tags')),
(INCLUDE_SUBSCRIBED, _('only subscribed tags')),
)
TAG_EMAIL_FILTER_FULL_STRATEGY_CHOICES = (
(INCLUDE_ALL, _('email for all tags')),
(EXCLUDE_IGNORED, _('exclude ignored tags')),
(INCLUDE_INTERESTING, _('only interesting tags')),
(INCLUDE_SUBSCRIBED, _('only subscribed tags')),
)
NOTIFICATION_DELIVERY_SCHEDULE_CHOICES = (
('i',_('instantly')),
('d',_('daily')),
('w',_('weekly')),
('n',_('no email')),
)
USERS_PAGE_SIZE = 28#todo: move it to settings?
USERNAME_REGEX_STRING = r'^[\w \-.@+\']+$'
GRAVATAR_TYPE_CHOICES = (
('identicon',_('identicon')),
('mm',_('mystery-man')),
('monsterid',_('monsterid')),
('wavatar',_('wavatar')),
('retro',_('retro')),
)
#chars that can go before or after @mention
TWITTER_STYLE_MENTION_TERMINATION_CHARS = '\n ;:,.!?<>"\''
COMMENT_HARD_MAX_LENGTH = 2048
#user status ch
USER_STATUS_CHOICES = (
#in addition to these there is administrator
#admin status is determined by the User.is_superuser() call
('m', _('moderator')), #user with moderation privilege
('a', _('approved')), #regular user
('w', _('watched')), #regular user placed on the moderation watch
('s', _('suspended')), #suspended user who cannot post new stuff
('b', _('blocked')), #blocked
)
DEFAULT_USER_STATUS = 'w'
#number of items to show in user views
USER_VIEW_DATA_SIZE = 50
#not really dependency, but external links, which it would
#be nice to test for correctness from time to time
DEPENDENCY_URLS = {
'akismet': 'https://akismet.com/signup/',
'cc-by-sa': 'http://creativecommons.org/licenses/by-sa/3.0/legalcode',
'embedding-video': \
'http://askbot.org/doc/optional-modules.html#embedding-video',
'favicon': 'http://en.wikipedia.org/wiki/Favicon',
'facebook-apps': 'http://www.facebook.com/developers/createapp.php',
'google-webmaster-tools': 'https://www.google.com/webmasters/tools/home',
'identica-apps': 'http://identi.ca/settings/oauthapps',
'noscript': 'https://www.google.com/support/bin/answer.py?answer=23852',
'linkedin-apps': 'https://www.linkedin.com/secure/developer',
'mathjax': 'http://www.mathjax.org/resources/docs/?installation.html',
'recaptcha': 'http://google.com/recaptcha',
'twitter-apps': 'http://dev.twitter.com/apps/',
}
PASSWORD_MIN_LENGTH = 6
GOLD_BADGE = 1
SILVER_BADGE = 2
BRONZE_BADGE = 3
BADGE_TYPE_CHOICES = (
(GOLD_BADGE, _('gold')),
(SILVER_BADGE, _('silver')),
(BRONZE_BADGE, _('bronze')),
)
BADGE_CSS_CLASSES = {
GOLD_BADGE: 'badge1',
SILVER_BADGE: 'badge2',
BRONZE_BADGE: 'badge3',
}
BADGE_DISPLAY_SYMBOL = '&#9679;'
MIN_REPUTATION = 1
AVATAR_STATUS_CHOICE = (
('n', _('None')),
('g', _('Gravatar')),#only if user has real uploaded gravatar
('a', _('Uploaded Avatar')),#avatar uploaded locally - with django-avatar app
)
SEARCH_ORDER_BY = (
('-added_at', _('date descendant')),
('added_at', _('date ascendant')),
('-last_activity_at', _('most recently active')),
('last_activity_at', _('least recently active')),
('-answer_count', _('more responses')),
('answer_count', _('fewer responses')),
('-points', _('more votes')),
('points', _('less votes')),
)
DEFAULT_QUESTION_WIDGET_STYLE = """
@import url('http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700');
body {
overflow: hidden;
}
#container {
width: 200px;
height: 350px;
}
ul {
list-style: none;
padding: 5px;
margin: 5px;
}
li {
border-bottom: #CCC 1px solid;
padding-bottom: 5px;
padding-top: 5px;
}
li:last-child {
border: none;
}
a {
text-decoration: none;
color: #464646;
font-family: 'Yanone Kaffeesatz', sans-serif;
font-size: 15px;
}
"""
#an exception import * because that file has only strings
from askbot.const.message_keys import *