summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorByron <byroncorrales@gmail.com>2011-09-19 11:34:44 -0600
committerByron <byroncorrales@gmail.com>2011-09-19 11:34:44 -0600
commit498bd123c080bed9935a6ccec048a1d15caf7ee6 (patch)
treef9c8fe28d5fd28e6c95c98fb2565db1c3aab5e04
parentadc55299eef92b42f125475b97c91d96ba41f5cc (diff)
parentfdaabe9a5d03493394bacac35f03694cbe781325 (diff)
downloadaskbot-498bd123c080bed9935a6ccec048a1d15caf7ee6.tar.gz
askbot-498bd123c080bed9935a6ccec048a1d15caf7ee6.tar.bz2
askbot-498bd123c080bed9935a6ccec048a1d15caf7ee6.zip
Merge branch 'master' of https://github.com/ASKBOT/askbot-devel
-rw-r--r--MANIFEST.in1
-rw-r--r--askbot/__init__.py2
-rw-r--r--askbot/conf/__init__.py2
-rw-r--r--askbot/conf/external_keys.py51
-rw-r--r--askbot/conf/license.py82
-rw-r--r--askbot/conf/sidebar_main.py10
-rw-r--r--askbot/conf/site_settings.py7
-rw-r--r--askbot/conf/skin_general_settings.py42
-rw-r--r--askbot/conf/spam_and_moderation.py32
-rw-r--r--askbot/const/__init__.py67
-rw-r--r--askbot/context.py4
-rw-r--r--askbot/deployment/path_utils.py13
-rw-r--r--askbot/deps/django_authopenid/backends.py16
-rw-r--r--askbot/doc/source/askbot/layout.html23
-rw-r--r--askbot/doc/source/askbot/static/facebook.pngbin0 -> 1453 bytes
-rw-r--r--askbot/doc/source/askbot/static/traditional.css67
-rw-r--r--askbot/doc/source/askbot/static/twitter.pngbin0 -> 1482 bytes
-rw-r--r--askbot/doc/source/changelog.rst25
-rw-r--r--askbot/doc/source/contributors.rst1
-rw-r--r--askbot/doc/source/customizing-skin-in-askbot.rst20
-rw-r--r--askbot/doc/source/index.rst6
-rw-r--r--askbot/doc/source/initialize-database-tables.rst10
-rw-r--r--askbot/doc/source/management-commands.rst6
-rw-r--r--askbot/importers/stackexchange/management/commands/load_stackexchange.py2
-rw-r--r--askbot/locale/en/LC_MESSAGES/django.po2
-rw-r--r--askbot/management/commands/clean_session.py50
-rw-r--r--askbot/management/commands/createsuperuser.py112
-rw-r--r--askbot/management/commands/update_avatar_data.py30
-rw-r--r--askbot/migrations/0005_install_badges.py9
-rw-r--r--askbot/migrations/0006_add_subscription_setting_for_comments_and_mentions.py3
-rw-r--r--askbot/migrations/0009_calculate_html_field_for_comments.py2
-rw-r--r--askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py3
-rw-r--r--askbot/migrations/0043_add_temporal_extra_column_for_datamigration.py308
-rw-r--r--askbot/migrations/0044_migrate_has_custom_avatar_field.py323
-rw-r--r--askbot/migrations/0045_delete_has_custom_avatar.py310
-rw-r--r--askbot/models/__init__.py76
-rw-r--r--askbot/models/question.py5
-rw-r--r--askbot/schedules.py19
-rw-r--r--askbot/skins/default/media/images/cc-by-sa.pngbin0 -> 5083 bytes
-rwxr-xr-xaskbot/skins/default/media/images/cc-wiki.pngbin2333 -> 0 bytes
-rw-r--r--askbot/skins/default/media/jquery-openid/jquery.openid.js18
-rw-r--r--askbot/skins/default/media/js/post.js33
-rw-r--r--askbot/skins/default/media/js/utils.js5
-rw-r--r--askbot/skins/default/media/style/jquery.autocomplete.css2
-rw-r--r--askbot/skins/default/media/style/style.css44
-rw-r--r--askbot/skins/default/templates/authopenid/providers_javascript.html12
-rw-r--r--askbot/skins/default/templates/authopenid/signin.html2
-rw-r--r--askbot/skins/default/templates/base.html5
-rw-r--r--askbot/skins/default/templates/blocks/bottom_scripts.html5
-rw-r--r--askbot/skins/default/templates/blocks/footer.html75
-rw-r--r--askbot/skins/default/templates/macros.html8
-rw-r--r--askbot/skins/default/templates/main_page/javascript.html10
-rw-r--r--askbot/skins/default/templates/tags.html1
-rw-r--r--askbot/skins/utils.py40
-rw-r--r--askbot/startup_procedures.py26
-rw-r--r--askbot/urls.py8
-rw-r--r--askbot/utils/console.py2
-rw-r--r--askbot/utils/decorators.py68
-rw-r--r--askbot/utils/functions.py24
-rw-r--r--askbot/utils/hasher.py43
-rw-r--r--askbot/views/readers.py2
-rw-r--r--askbot/views/users.py13
-rw-r--r--askbot/views/writers.py22
-rw-r--r--askbot_requirements.txt1
-rw-r--r--setup.py1
65 files changed, 1966 insertions, 245 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
index 484f6b67..7d503fb7 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,6 +4,7 @@ include LICENSE
include AUTHORS
include COPYING
include README.rst
+include askbot_requirements.txt
recursive-include askbot *
recursive-exclude askbot *.pyc
recursive-exclude .git
diff --git a/askbot/__init__.py b/askbot/__init__.py
index d56557d9..8133c74f 100644
--- a/askbot/__init__.py
+++ b/askbot/__init__.py
@@ -9,7 +9,7 @@ import smtplib
import sys
import logging
-VERSION = (0, 7, 20)
+VERSION = (0, 7, 22)
#necessary for interoperability of django and coffin
try:
diff --git a/askbot/conf/__init__.py b/askbot/conf/__init__.py
index b19aeb60..1d2d7240 100644
--- a/askbot/conf/__init__.py
+++ b/askbot/conf/__init__.py
@@ -7,11 +7,13 @@ import askbot.conf.email
import askbot.conf.forum_data_rules
import askbot.conf.flatpages
import askbot.conf.site_settings
+import askbot.conf.license
import askbot.conf.external_keys
import askbot.conf.skin_general_settings
import askbot.conf.sidebar_main
import askbot.conf.sidebar_question
import askbot.conf.sidebar_profile
+import askbot.conf.spam_and_moderation
import askbot.conf.user_settings
import askbot.conf.markup
import askbot.conf.social_sharing
diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py
index d20bf689..8912b0ff 100644
--- a/askbot/conf/external_keys.py
+++ b/askbot/conf/external_keys.py
@@ -1,6 +1,5 @@
-"""
-External service key settings
-"""
+"""External service key settings"""
+from askbot import const
from askbot.conf.settings_wrapper import settings
from askbot.deps import livesettings
from django.utils.translation import ugettext as _
@@ -8,7 +7,8 @@ from django.conf import settings as django_settings
EXTERNAL_KEYS = livesettings.ConfigurationGroup(
'EXTERNAL_KEYS',
- _('Keys to connect the site with external services like Facebook, etc.')
+ _('Keys to connect the site with external '
+ 'services like Facebook, etc.')
)
settings.register(
@@ -19,11 +19,12 @@ settings.register(
help_text=_(
'This key helps google index your site '
'please obtain is at '
- '<a href="%(google_webmasters_tools_url)s">'
+ '<a href="%(url)s?hl=%(lang)s">'
'google webmasters tools site</a>'
- ) % {'google_webmasters_tools_url':
- 'https://www.google.com/webmasters/tools/home?hl=' \
- + django_settings.LANGUAGE_CODE}
+ ) % {
+ 'url': const.DEPENDENCY_URLS['google-webmaster-tools'],
+ 'lang': django_settings.LANGUAGE_CODE,
+ }
)
)
@@ -33,12 +34,12 @@ settings.register(
'GOOGLE_ANALYTICS_KEY',
description=_('Google Analytics key'),
help_text=_(
- 'Obtain is at <a href="%(ga_site)s">'
- 'Google Analytics</a> site, if you '
- 'wish to use Google Analytics to monitor '
- 'your site'
- ) % {'ga_site':'http://www.google.com/intl/%s/analytics/' \
- % django_settings.LANGUAGE_CODE }
+ 'Obtain is at <a href="%(url)s">'
+ 'Google Analytics</a> site, if you '
+ 'wish to use Google Analytics to monitor '
+ 'your site'
+ ) % {'url': 'http://www.google.com/intl/%s/analytics/' \
+ % django_settings.LANGUAGE_CODE }
)
)
@@ -68,8 +69,8 @@ settings.register(
'Recaptcha is a tool that helps distinguish '
'real people from annoying spam robots. '
'Please get this and a public key at '
- 'the <a href="http://recaptcha.net">recaptcha.net</a>'
- )
+ 'the <a href="%(url)s">%(url)s</a>'
+ ) % {'url': const.DEPENDENCY_URLS['recaptcha']}
)
)
@@ -82,9 +83,9 @@ settings.register(
'Facebook API key and Facebook secret '
'allow to use Facebook Connect login method '
'at your site. Please obtain these keys '
- 'at <a href="http://www.facebook.com/developers/createapp.php">'
+ 'at <a href="%(url)s">'
'facebook create app</a> site'
- )
+ ) % {'url': const.DEPENDENCY_URLS['facebook-apps']}
)
)
@@ -102,9 +103,9 @@ settings.register(
'TWITTER_KEY',
description=_('Twitter consumer key'),
help_text=_(
- 'Please register your forum at <a href="http://dev.twitter.com/apps/">'
+ 'Please register your forum at <a href="%(url)s">'
'twitter applications site</a>'
- ),
+ ) % {'url': const.DEPENDENCY_URLS['twitter-apps']},
)
)
@@ -123,9 +124,9 @@ settings.register(
'LINKEDIN_KEY',
description=_('LinkedIn consumer key'),
help_text=_(
- 'Please register your forum at <a href="http://dev.twitter.com/apps/">'
- 'twitter applications site</a>'
- ),
+ 'Please register your forum at <a href="%(url)s">'
+ 'LinkedIn developer site</a>'
+ ) % {'url': const.DEPENDENCY_URLS['linkedin-apps']},
)
)
@@ -144,9 +145,9 @@ settings.register(
'IDENTICA_KEY',
description=_('ident.ca consumer key'),
help_text=_(
- 'Please register your forum at <a href="http://identi.ca/settings/oauthapps">'
+ 'Please register your forum at <a href="%(url)s">'
'Identi.ca applications site</a>'
- ),
+ ) % {'url': const.DEPENDENCY_URLS['identica-apps']},
)
)
diff --git a/askbot/conf/license.py b/askbot/conf/license.py
new file mode 100644
index 00000000..73001c2d
--- /dev/null
+++ b/askbot/conf/license.py
@@ -0,0 +1,82 @@
+"""settings that allow changing of the license
+clause used in askbot instances"""
+from askbot import const
+from askbot.conf.settings_wrapper import settings
+from askbot.deps import livesettings
+from askbot.skins import utils as skin_utils
+from django.utils.translation import ugettext as _
+from django.conf import settings as django_settings
+
+LICENSE_SETTINGS = livesettings.ConfigurationGroup(
+ 'LICENSE_SETTINGS',
+ _('License settings')
+ )
+
+settings.register(
+ livesettings.BooleanValue(
+ LICENSE_SETTINGS,
+ 'USE_LICENSE',
+ description = _('Show license clause in the site footer'),
+ default = True
+ )
+)
+
+settings.register(
+ livesettings.StringValue(
+ LICENSE_SETTINGS,
+ 'LICENSE_ACRONYM',
+ description = _('Short name for the license'),
+ default = 'cc-by-sa'
+ )
+)
+
+settings.register(
+ livesettings.StringValue(
+ LICENSE_SETTINGS,
+ 'LICENSE_TITLE',
+ description = _('Full name of the license'),
+ default = _('Creative Commons Attribution Share Alike 3.0'),
+ )
+)
+
+settings.register(
+ livesettings.BooleanValue(
+ LICENSE_SETTINGS,
+ 'LICENSE_USE_URL',
+ description = _('Add link to the license page'),
+ default = True
+ )
+)
+
+settings.register(
+ livesettings.URLValue(
+ LICENSE_SETTINGS,
+ 'LICENSE_URL',
+ description = _('License homepage'),
+ help_text = _(
+ 'URL of the official page with all the license legal clauses'
+ ),
+ default = const.DEPENDENCY_URLS['cc-by-sa']
+ )
+)
+
+settings.register(
+ livesettings.BooleanValue(
+ LICENSE_SETTINGS,
+ 'LICENSE_USE_LOGO',
+ description = _('Use license logo'),
+ default = True
+ )
+)
+
+settings.register(
+ livesettings.ImageValue(
+ LICENSE_SETTINGS,
+ 'LICENSE_LOGO_URL',
+ description = _('License logo image'),
+ upload_directory = django_settings.ASKBOT_FILE_UPLOAD_DIR,
+ upload_url = '/' + django_settings.ASKBOT_UPLOADED_FILES_URL,
+ default = '/images/cc-by-sa.png',
+ url_resolver = skin_utils.get_media_url
+ )
+)
diff --git a/askbot/conf/sidebar_main.py b/askbot/conf/sidebar_main.py
index 012d8ace..57f9848d 100644
--- a/askbot/conf/sidebar_main.py
+++ b/askbot/conf/sidebar_main.py
@@ -41,6 +41,16 @@ settings.register(
)
settings.register(
+ values.IntegerValue(
+ SIDEBAR_MAIN,
+ 'SIDEBAR_MAIN_AVATAR_LIMIT',
+ description = _('Limit how many avatars will be displayed on the sidebar'),
+ default = 16
+ )
+)
+
+
+settings.register(
values.BooleanValue(
SIDEBAR_MAIN,
'SIDEBAR_MAIN_SHOW_TAG_SELECTOR',
diff --git a/askbot/conf/site_settings.py b/askbot/conf/site_settings.py
index 804f457c..376c65c0 100644
--- a/askbot/conf/site_settings.py
+++ b/askbot/conf/site_settings.py
@@ -17,7 +17,7 @@ settings.register(
livesettings.StringValue(
QA_SITE_SETTINGS,
'APP_TITLE',
- default=u'ASKBOT: Open Source Q&A Forum',
+ default=u'Askbot: Open Source Q&A Forum',
description=_('Site title for the Q&A forum')
)
)
@@ -26,7 +26,7 @@ settings.register(
livesettings.StringValue(
QA_SITE_SETTINGS,
'APP_KEYWORDS',
- default=u'ASKBOT,CNPROG,forum,community',
+ default=u'Askbot,CNPROG,forum,community',
description=_('Comma separated list of Q&A site keywords')
)
)
@@ -35,8 +35,7 @@ settings.register(
livesettings.StringValue(
QA_SITE_SETTINGS,
'APP_COPYRIGHT',
- default='Copyright ASKBOT, 2010. Some rights reserved ' + \
- 'under creative commons license.',
+ default='Copyright Askbot, 2010-2011.',
description=_('Copyright message to show in the footer')
)
)
diff --git a/askbot/conf/skin_general_settings.py b/askbot/conf/skin_general_settings.py
index b90d3de5..8521bf87 100644
--- a/askbot/conf/skin_general_settings.py
+++ b/askbot/conf/skin_general_settings.py
@@ -106,20 +106,7 @@ settings.register(
)
)
-settings.register(
- values.IntegerValue(
- GENERAL_SKIN_SETTINGS,
- 'MEDIA_RESOURCE_REVISION',
- default = 1,
- description = _('Skin media revision number'),
- help_text = _(
- 'Increment this number when you change '
- 'image in skin media or stylesheet. '
- 'This helps avoid showing your users '
- 'outdated images from their browser cache.'
- )
- )
-)
+
settings.register(
values.BooleanValue(
@@ -271,3 +258,30 @@ settings.register(
)
)
)
+
+settings.register(
+ values.IntegerValue(
+ GENERAL_SKIN_SETTINGS,
+ 'MEDIA_RESOURCE_REVISION',
+ default = 1,
+ description = _('Skin media revision number'),
+ help_text = _(
+ 'Will be set automatically '
+ 'but you can modify it if necessary.'
+ )
+ )
+)
+
+settings.register(
+ values.StringValue(
+ GENERAL_SKIN_SETTINGS,
+ 'MEDIA_RESOURCE_REVISION_HASH',
+ description = _(
+ 'Hash to update the media revision number automatically.'
+ ),
+ default='',
+ help_text = _(
+ 'Will be set automatically, it is not necesary to modify manually.'
+ )
+ )
+)
diff --git a/askbot/conf/spam_and_moderation.py b/askbot/conf/spam_and_moderation.py
new file mode 100644
index 00000000..375fbdd5
--- /dev/null
+++ b/askbot/conf/spam_and_moderation.py
@@ -0,0 +1,32 @@
+"""Settings for content moderation and spam control"""
+from django.utils.translation import ugettext as _
+from askbot import const
+from askbot.deps import livesettings
+from askbot.conf.settings_wrapper import settings
+
+SPAM_AND_MODERATION = livesettings.ConfigurationGroup(
+ 'SPAM_AND_MODERATION',
+ _('Spam control and content moderation')
+ )
+
+settings.register(
+ livesettings.BooleanValue(
+ SPAM_AND_MODERATION,
+ 'USE_AKISMET',
+ description=_('Enable Akismet spam detection(keys below are required)'),
+ default=False,
+ help_text = _(
+ 'To get an Akismet key please visit '
+ '<a href="%(url)s">Akismet site</a>'
+ ) % {'url': const.DEPENDENCY_URLS['akismet']}
+ )
+)
+
+settings.register(
+ livesettings.StringValue(
+ SPAM_AND_MODERATION,
+ 'AKISMET_API_KEY',
+ description=_('Akismet key for spam detection')
+ )
+)
+
diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py
index 15233ee1..73c71800 100644
--- a/askbot/const/__init__.py
+++ b/askbot/const/__init__.py
@@ -1,10 +1,11 @@
# encoding:utf-8
-from django.utils.translation import ugettext as _
-import re
"""
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.
+For reasons that models, views can't have unicode
+text in this project, all unicode text go here.
"""
+from django.utils.translation import ugettext as _
+import re
CLOSE_REASONS = (
(1, _('duplicate question')),
(2, _('question is off-topic or not relevant')),
@@ -48,7 +49,8 @@ POST_SORT_METHODS = (
('relevance-desc', _('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
+#because they are keys to the hash used in implementations
+#of Q.run_advanced_search
DEFAULT_POST_SORT_METHOD = 'activity-desc'
POST_SCOPE_LIST = (
@@ -91,28 +93,28 @@ TAG_REGEX = r'^[%s]+$' % TAG_CHARS
TAG_SPLIT_REGEX = r'[ ,]+'
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_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_EDIT_QUESTION=17
-#TYPE_ACTIVITY_EDIT_ANSWER=18
+#TYPE_ACTIVITY_EDIT_QUESTION = 17
+#TYPE_ACTIVITY_EDIT_ANSWER = 18
#todo: rename this to TYPE_ACTIVITY_CHOICES
TYPE_ACTIVITY = (
@@ -250,10 +252,22 @@ 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 = {
- 'mathjax': 'http://www.mathjax.org/resources/docs/?installation.html',
+ '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',
- 'embedding-video': 'http://askbot.org/doc/optional-modules.html#embedding-video'
+ '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 = 8
@@ -275,6 +289,11 @@ BADGE_DISPLAY_SYMBOL = '&#9679;'
MIN_REPUTATION = 1
+AVATAR_STATUS_CHOICE = (
+ ('n', _('None')),
+ ('g', _('Gravatar')),
+ ('a', _('Uploaded Avatar')),
+)
#an exception import * because that file has only strings
from askbot.const.message_keys import *
diff --git a/askbot/context.py b/askbot/context.py
index 5a174585..6dc38f79 100644
--- a/askbot/context.py
+++ b/askbot/context.py
@@ -5,6 +5,7 @@ and the application available for the templates
from django.conf import settings
import askbot
from askbot import api
+from askbot import const
from askbot.conf import settings as askbot_settings
from askbot.skins.loaders import get_skin
from askbot.utils import url_utils
@@ -22,5 +23,6 @@ def application_settings(request):
return {
'settings': my_settings,
'skin': get_skin(request),
- 'moderation_items': api.get_info_on_moderation_items(request.user)
+ 'moderation_items': api.get_info_on_moderation_items(request.user),
+ 'noscript_url': const.DEPENDENCY_URLS['noscript'],
}
diff --git a/askbot/deployment/path_utils.py b/askbot/deployment/path_utils.py
index 7a1197de..6ad9fc99 100644
--- a/askbot/deployment/path_utils.py
+++ b/askbot/deployment/path_utils.py
@@ -101,6 +101,7 @@ def path_is_clean_for_django(directory):
def create_path(directory):
+ """equivalent to mkdir -p"""
if os.path.isdir(directory):
return
elif os.path.exists(directory):
@@ -109,12 +110,17 @@ def create_path(directory):
os.makedirs(directory)
def touch(file_path, times = None):
+ """implementation of unix ``touch`` in python"""
#http://stackoverflow.com/questions/1158076/implement-touch-using-python
- with file(file_path, 'a'):
- os.utime(file_path, times)
+ fhandle = file(file_path, 'a')
+ try:
+ os.utime(file_path, times)
+ finally:
+ fhandle.close()
SOURCE_DIR = os.path.dirname(os.path.dirname(__file__))
def get_path_to_help_file():
+ """returns path to the main plain text help file"""
return os.path.join(SOURCE_DIR, 'doc', 'INSTALL')
def deploy_into(directory, new_project = None):
@@ -145,7 +151,7 @@ def deploy_into(directory, new_project = None):
print ''
app_dir = os.path.join(directory, 'askbot')
- copy_dirs = ('doc','cron','upfiles')
+ copy_dirs = ('doc', 'cron', 'upfiles')
dirs_copied = 0
for dir_name in copy_dirs:
src = os.path.join(SOURCE_DIR, dir_name)
@@ -165,6 +171,7 @@ def deploy_into(directory, new_project = None):
print ''
def dir_name_acceptable(directory):
+ """True if directory is not taken by another python module"""
dir_name = os.path.basename(directory)
try:
imp.find_module(dir_name)
diff --git a/askbot/deps/django_authopenid/backends.py b/askbot/deps/django_authopenid/backends.py
index e608086c..9f8f1dfd 100644
--- a/askbot/deps/django_authopenid/backends.py
+++ b/askbot/deps/django_authopenid/backends.py
@@ -3,6 +3,7 @@ multiple login methods supported by the authenticator
application
"""
import datetime
+import logging
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import ugettext as _
@@ -48,7 +49,20 @@ class AuthBackend(object):
if not user.check_password(password):
return None
except User.DoesNotExist:
- return None
+ try:
+ email_address = username
+ user = User.objects.get(email = email_address)
+ if not user.check_password(password):
+ return None
+ except User.DoesNotExist:
+ return None
+ except User.MultipleObjectsReturned:
+ logging.critical(
+ ('have more than one user with email %s ' +
+ 'he/she will not be able to authenticate with ' +
+ 'the email address in the place of user name') % email_address
+ )
+ return None
else:
if login_providers[provider_name]['check_password'](username, password):
try:
diff --git a/askbot/doc/source/askbot/layout.html b/askbot/doc/source/askbot/layout.html
index 251d8386..f1c8b509 100644
--- a/askbot/doc/source/askbot/layout.html
+++ b/askbot/doc/source/askbot/layout.html
@@ -1,16 +1,19 @@
{% extends "basic/layout.html" %}
{% block relbar1 %}
+<div class="logo">
+ <a href="/doc/index.html"><h1>Askbot Project Documentation</h1></a>
+ <div class="topnav">
+ <a href="http://twitter.com/askbot7" alt="Follow us on twitter"><img src="_static/twitter.png"/></a>
+ <a href="https://www.facebook.com/pages/Askbot/128297183877495" alt="like us on Facebook"><img src="_static/facebook.png"/></a>
+ </div>
+</div>
<div class="ab-proj-header">
- <ul>
- <li class="first"><a href="/">Home (forum)</a>
- <span class="sep">|</span>
- </li>
- {#<li><a href="/doc/about.html">About</a></li>
- <span class="sep">|</span>#}
- <li><a href="/doc/index.html">Documentation</a></li>
- {#<span class="sep">|</span>
- <li><a href="/doc/download.html">Download</a></li>#}
- </ul>
+ <a href="/">Home</a> |
+ <a href="/en/questions/" title="Ask Questions">Ask Questions</a> |
+ <a href="/hire-us" alt='Hire Us'>Hire Us</a> |
+ <a href="/doc/index.html" alt="Documentation">Documentation</a> |
+ <a href="/contribute" alt='Contribute'>Contribute</a> |
+ <a href="/feedback/" alt='contact'>Contact</a>
</div>
{% endblock %}
{% block relbar2 %}
diff --git a/askbot/doc/source/askbot/static/facebook.png b/askbot/doc/source/askbot/static/facebook.png
new file mode 100644
index 00000000..77ae358f
--- /dev/null
+++ b/askbot/doc/source/askbot/static/facebook.png
Binary files differ
diff --git a/askbot/doc/source/askbot/static/traditional.css b/askbot/doc/source/askbot/static/traditional.css
index cfbdaf79..3fa381cd 100644
--- a/askbot/doc/source/askbot/static/traditional.css
+++ b/askbot/doc/source/askbot/static/traditional.css
@@ -8,11 +8,13 @@
* :license: BSD, see LICENSE for details.
*
*/
+@import url(http://fonts.googleapis.com/css?family=Droid+Sans|Cabin|Cabin+Sketch:700);
body {
color: #000;
margin: 0;
padding: 0;
+ border-color: gray;
}
/* :::: LAYOUT :::: */
@@ -29,11 +31,11 @@ div.bodywrapper {
div.body {
background-color: white;
padding: 0 20px 30px 0;
- font-family: "Lucida Grande",Verdana,"Bitstream Vera Sans",Arial,sans-serif;
+ font-family: "Droid Sans", "Lucida Grande",Verdana,"Bitstream Vera Sans",Arial,sans-serif;
}
div.sphinxsidebarwrapper {
- border: 1px solid #99ccff;
+ border: 1px solid #777;
padding: 10px;
margin: 55px 15px 10px 0;
}
@@ -42,6 +44,7 @@ div.sphinxsidebar {
float: right;
margin-left: -100%;
width: 230px;
+ font-family: "Droid Sans", sans-serif;
}
div.clearer {
@@ -59,6 +62,7 @@ div.footer {
background-color: #dcdcdc;
padding: 9px 0 9px 0;
text-align: center;
+ font-family: "Droid Sans", sans-serif;
}
div.related {
@@ -92,6 +96,11 @@ div.related li.right {
}
/* ::: SIDEBAR :::: */
+div.sphinxsidebar h3,
+div.sphinxsidebar h4{
+ font-family: 'Cabin', cursive;
+}
+
div.sphinxsidebar h3 {
margin: 0;
}
@@ -100,10 +109,6 @@ div.sphinxsidebar h4 {
margin: 5px 0 0 0;
}
-div.sphinxsidebar p,
-div.sphinxsidebar a {
- font-family: "Lucida Grande",Verdana,"Bitstream Vera Sans",Arial,sans-serif;
-}
div.sphinxsidebar p.topless {
margin: 5px 10px 10px 10px;
@@ -332,11 +337,10 @@ a {
text-decoration: none;
border-bottom: 1px solid #dfdfdf;
}
-a:link:active { color: #ff0000; }
-a:link:hover { background-color: #bbeeff; }
-a:visited:hover { background-color: #bbeeff; }
-a:visited { color: #3185AB; }
-a:link { color: #21759B; }
+a:link:active { color: #555; }
+a:link:hover { color: #000; }
+a:visited { color: #655; }
+a:link { color: #555; }
div.body h1,
div.body h2,
@@ -344,7 +348,7 @@ div.body h3,
div.body h4,
div.body h5,
div.body h6 {
- font-family: Georgia, Times, "Times New Roman", serif;
+ font-family: 'Cabin', cursive;
font-weight: bold;
}
@@ -655,23 +659,36 @@ div.viewcode-block:target {
div.ab-proj-header {
width: 960px;
margin: auto;
- font-family: "Lucida Grande",Tahoma,"Bitstream Vera Sans",Arial,sans-serif;
-}
-div.ab-proj-header li {
- display: inline;
- padding: 0;
+ border-bottom: 2px solid #555;
+ font-family: "Droid Sans", sans-serif;
}
+
div.ab-proj-header a,
-div.ab-proj-header a:visited,
+div.ab-proj-header a:visited
{
text-decoration: none;
+ color: #333;
+ border: none;
+ font-family: "Droid Sans", sans-serif;
}
-div.ab-proj-header ul {
- margin: 0;
- padding: 5px 0 0 0;
- list-style: none;
+
+div.logo{
+ font-size: 20px;
+ width: 960px;
+ margin: auto;
}
-div.ab-proj-header .sep {
- color: #aaa;
- padding: 0 10px;
+
+div.logo a, div.logo a:visited
+{
+ text-decoration: none;
+ color: #333;
+ border: none;
+ font-family: 'Cabin Sketch', cursive;
+}
+
+div.topnav{
+ float: right;
+ position: relative;
+ right:6px;
+ top:-65px;
}
diff --git a/askbot/doc/source/askbot/static/twitter.png b/askbot/doc/source/askbot/static/twitter.png
new file mode 100644
index 00000000..371c9823
--- /dev/null
+++ b/askbot/doc/source/askbot/static/twitter.png
Binary files differ
diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst
index 523a011f..c1eaa158 100644
--- a/askbot/doc/source/changelog.rst
+++ b/askbot/doc/source/changelog.rst
@@ -1,12 +1,29 @@
Changes in Askbot
=================
-Development version (not yet on pypi)
--------------------------------------
-* First user automatically becomes site administrator (Adolfo Fitoria)
+Development version
+-------------------
+* Added support for Akismet spam detection service (Adolfo Fitoria)
+* Added noscript message (Arun SAG)
+* Support for url shortening with TinyUrl on link sharing (Rtnpro)
+* Allowed logging in with password and email in the place of login name (Evgeny)
+* Added config settings allowing adjust license information (Evgeny)
-0.7.20 (Current Version)
+0.7.22 (Current Version)
------------------------
+* Media resource revision is now incremented
+ automatically any time when media is updated (Adolfo Fitoria, Evgeny Fadeev)
+* First user automatically becomes site administrator (Adolfo Fitoria)
+* Avatar displayed on the sidebar can be controlled with livesettings.(Adolfo Fitoria, Evgeny Fadeev)
+* Avatar box in the sidebar is ordered with priority for real faces.(Adolfo Fitoria)
+* Django's createsuperuser now works with askbot (Adolfo Fitoria)
+
+0.7.21
+------
+This version was skipped
+
+0.7.20
+------
* Added support for login via self-hosted Wordpress site (Adolfo Fitoria)
* Allowed basic markdown in the comments (Adolfo Fitoria)
* Added this changelog (Adolfo Fitoria)
diff --git a/askbot/doc/source/contributors.rst b/askbot/doc/source/contributors.rst
index c4c443d7..ea7ee34e 100644
--- a/askbot/doc/source/contributors.rst
+++ b/askbot/doc/source/contributors.rst
@@ -11,6 +11,7 @@ Programming and documentation
* Mike Chen & Sailing Cai - original authors of CNPROG forum
* Evgeny Fadeev - founder of askbot
* `Adolfo Fitoria <http://fitoria.net>`_
+* `Arun SAG <http://zer0c00l.in/>`_
* Andy Knotts
* Benoit Lavine (with Windriver Software, Inc.)
* Jeff Madynski
diff --git a/askbot/doc/source/customizing-skin-in-askbot.rst b/askbot/doc/source/customizing-skin-in-askbot.rst
index 96c2ec9c..686fc3ed 100644
--- a/askbot/doc/source/customizing-skin-in-askbot.rst
+++ b/askbot/doc/source/customizing-skin-in-askbot.rst
@@ -56,7 +56,7 @@ Possible approaches to customize skins
There are several methods at your disposal,
would you like to customize askbot's appearance.
-.. note::
+.. deprecated:: 0.7.21
Whenever you change any media files on disk, it will be necessary
to increment "skin media revision number" in the
skin settings and restart the app,
@@ -120,13 +120,29 @@ by from the root account.
Create a custom skin in a new directory
---------------------------------------
This is technically possible, but not advisable
-because a redesign of default skin is expected.
+because a redesign of default skin is pending.
+After the redesign your custom skins may be difficult
+to update.
If you still wish to follow this option,
name all directories and files the same way as
in the "default" skin, as some template file names are
hard-coded in the askbot's python code.
+Add setting ``ASKBOT_EXTRA_SKINS_DIR`` to your ``settings.py`` file
+and set its value to the directory with your additional skins.
+
+For example::
+
+ ASKBOT_EXTRA_SKINS_DIR = '/home/myname/my_askbot_themes'
+
+And your directory structure might be::
+
+ /home/myname/my_askbot_themes/
+ /my_theme
+ /templates
+ /media
+
If you are planning to seriously recode the skin -
it will be worthwhile learning the ``git`` system
and just follow the recipe described in the previous section -
diff --git a/askbot/doc/source/index.rst b/askbot/doc/source/index.rst
index 98dbf777..27f106be 100644
--- a/askbot/doc/source/index.rst
+++ b/askbot/doc/source/index.rst
@@ -1,8 +1,8 @@
.. _index:
-=============================
-Askbot Project Documentation
-=============================
+==================
+Documentation Home
+==================
Askbot is an open source Question and Answer (Q&A) forum project inspired by StackOverflow
and YahooAnswers.
diff --git a/askbot/doc/source/initialize-database-tables.rst b/askbot/doc/source/initialize-database-tables.rst
index f1281816..a296de3e 100644
--- a/askbot/doc/source/initialize-database-tables.rst
+++ b/askbot/doc/source/initialize-database-tables.rst
@@ -8,7 +8,11 @@ When you install Askbot the first time and any time you upgrade the software, ru
python manage.py syncdb
-When the script asks you if you want to create a superuser, answer **no**.
+.. versionchanged:: 0.7.21
+ When the script asks you if you want to create a superuser, answer yes if you want to create one. By default Askbot sets admin status(superuser) for the first user created automatically but also supports this form.
+
+.. deprecated:: 0.7.21
+ When the script asks you if you want to create a superuser, answer **no**.
Then run::
@@ -38,12 +42,12 @@ Connect to the Django development server with your Web browser. The address is t
Once the fresh copy of Askbot appears in your browser, create a new account at the site.
This will be your administrator account.
-.. deprecated:: 0.7.20.
+.. deprecated:: 0.7.20
Finally, turn the newly added user into a superuser by running::
python manage.py add_admin 1
-.. versionadded:: 0.7.20.
+.. versionadded:: 0.7.20
In the new version of Askbot the first user you create on the site will be added as administrator.
Here number 1 is the numeric id of the first user, enter a different number, if it is indeed different.
diff --git a/askbot/doc/source/management-commands.rst b/askbot/doc/source/management-commands.rst
index a91c0286..bc32dbc2 100644
--- a/askbot/doc/source/management-commands.rst
+++ b/askbot/doc/source/management-commands.rst
@@ -72,6 +72,12 @@ The bulk of the management commands fall into this group and will probably be th
| | , including the questions that are themselves |
| | marked as deleted. |
+---------------------------------+-------------------------------------------------------------+
+| `update_avatar_data` | Set values of avatar types for all users; |
+| | this command may take up to 2s per user, because it makes |
+| | up to one http request per user to gravatar.com. |
+| | This data is used to display preferentially real faces |
+| | on the main page. |
++---------------------------------+-------------------------------------------------------------+
.. _email-related-commands:
diff --git a/askbot/importers/stackexchange/management/commands/load_stackexchange.py b/askbot/importers/stackexchange/management/commands/load_stackexchange.py
index 20155e36..1f6b2042 100644
--- a/askbot/importers/stackexchange/management/commands/load_stackexchange.py
+++ b/askbot/importers/stackexchange/management/commands/load_stackexchange.py
@@ -890,7 +890,7 @@ class Command(BaseCommand):
u_openid.save()
except AssertionError:
print u'User %s (id=%d) does not have openid' % \
- (se_u.display_name, se_u.id)
+ (unidecode(se_u.display_name), se_u.id)
sys.stdout.flush()
except IntegrityError:
print "Warning: have duplicate openid: %s" % se_u.open_id
diff --git a/askbot/locale/en/LC_MESSAGES/django.po b/askbot/locale/en/LC_MESSAGES/django.po
index 5bb75eca..a1548a93 100644
--- a/askbot/locale/en/LC_MESSAGES/django.po
+++ b/askbot/locale/en/LC_MESSAGES/django.po
@@ -499,7 +499,7 @@ msgstr ""
#: conf/email.py:22
msgid "Prefix for the email subject line"
-msgstr "Welcome to the Q&A forum"
+msgstr ""
#: conf/email.py:24
msgid ""
diff --git a/askbot/management/commands/clean_session.py b/askbot/management/commands/clean_session.py
new file mode 100644
index 00000000..6ba9352c
--- /dev/null
+++ b/askbot/management/commands/clean_session.py
@@ -0,0 +1,50 @@
+from django.core.management.base import NoArgsCommand
+from django.contrib.sessions.models import Session
+from django.db import transaction
+from optparse import make_option
+from askbot.utils.console import print_progress
+from datetime import datetime
+
+DELETE_LIMIT = 1000
+
+class Command(NoArgsCommand):
+ option_list = NoArgsCommand.option_list + (
+ make_option('--quiet',
+ action='store_true',
+ dest='quiet',
+ default=False,
+ help="Do not print anything when called."
+ ),
+ )
+
+ @transaction.commit_manually
+ def handle_noargs(self, **options):
+ '''deletes old sessions'''
+ quiet = options.get('quiet', False)
+ expired_session_count = Session.objects.filter(expire_date__lt=datetime.now()).count()
+ expired_session_list= Session.objects.filter(expire_date__lt=datetime.now()).values_list('session_key', flat=True)
+ transaction.commit()
+
+ if not quiet:
+ print "There are %d expired sessions" % expired_session_count
+
+ range_limit = len(expired_session_list) - 1
+ higher_limit = lower_limit = 0
+
+ for i in range(DELETE_LIMIT, range_limit, DELETE_LIMIT):
+ lower_limit = i
+ higher_limit = lower_limit + DELETE_LIMIT
+ sublist = expired_session_list[lower_limit:higher_limit]
+ Session.objects.filter(session_key__in = sublist).delete()
+ transaction.commit()
+ if not quiet:
+ print_progress(higher_limit-1, expired_session_count)
+
+ if higher_limit < expired_session_list:
+ sublist = expired_session_list[higher_limit:expired_session_count]
+ Session.objects.filter(session_key__in = sublist).delete()
+ print_progress(expired_session_count, expired_session_count)
+ transaction.commit()
+
+ if not quiet:
+ print "sessions cleared"
diff --git a/askbot/management/commands/createsuperuser.py b/askbot/management/commands/createsuperuser.py
new file mode 100644
index 00000000..eb363bbd
--- /dev/null
+++ b/askbot/management/commands/createsuperuser.py
@@ -0,0 +1,112 @@
+from django.contrib.auth.management.commands.createsuperuser import *
+from django.db.models.signals import pre_save, post_save
+
+class Command(Command):
+
+ def handle(self, *args, **options):
+ username = options.get('username', None)
+ email = options.get('email', None)
+ interactive = options.get('interactive')
+ verbosity = int(options.get('verbosity', 1))
+
+ # Do quick and dirty validation if --noinput
+ if not interactive:
+ if not username or not email:
+ raise CommandError("You must use --username and --email with --noinput.")
+ if not RE_VALID_USERNAME.match(username):
+ raise CommandError("Invalid username. Use only letters, digits, and underscores")
+ try:
+ is_valid_email(email)
+ except exceptions.ValidationError:
+ raise CommandError("Invalid email address.")
+
+ # If not provided, create the user with an unusable password
+ password = None
+
+ # Try to determine the current system user's username to use as a default.
+ try:
+ default_username = getpass.getuser().replace(' ', '').lower()
+ except (ImportError, KeyError):
+ # KeyError will be raised by os.getpwuid() (called by getuser())
+ # if there is no corresponding entry in the /etc/passwd file
+ # (a very restricted chroot environment, for example).
+ default_username = ''
+
+ # Determine whether the default username is taken, so we don't display
+ # it as an option.
+ if default_username:
+ try:
+ User.objects.get(username=default_username)
+ except User.DoesNotExist:
+ pass
+ else:
+ default_username = ''
+
+ # Prompt for username/email/password. Enclose this whole thing in a
+ # try/except to trap for a keyboard interrupt and exit gracefully.
+ if interactive:
+ try:
+
+ # Get a username
+ while 1:
+ if not username:
+ input_msg = 'Username'
+ if default_username:
+ input_msg += ' (Leave blank to use %r)' % default_username
+ username = raw_input(input_msg + ': ')
+ if default_username and username == '':
+ username = default_username
+ if not RE_VALID_USERNAME.match(username):
+ sys.stderr.write("Error: That username is invalid. Use only letters, digits and underscores.\n")
+ username = None
+ continue
+ try:
+ User.objects.get(username=username)
+ except User.DoesNotExist:
+ break
+ else:
+ sys.stderr.write("Error: That username is already taken.\n")
+ username = None
+
+ # Get an email
+ while 1:
+ if not email:
+ email = raw_input('E-mail address: ')
+ try:
+ is_valid_email(email)
+ except exceptions.ValidationError:
+ sys.stderr.write("Error: That e-mail address is invalid.\n")
+ email = None
+ else:
+ break
+
+ # Get a password
+ while 1:
+ if not password:
+ password = getpass.getpass()
+ password2 = getpass.getpass('Password (again): ')
+ if password != password2:
+ sys.stderr.write("Error: Your passwords didn't match.\n")
+ password = None
+ continue
+ if password.strip() == '':
+ sys.stderr.write("Error: Blank passwords aren't allowed.\n")
+ password = None
+ continue
+ break
+ except KeyboardInterrupt:
+ sys.stderr.write("\nOperation cancelled.\n")
+ sys.exit(1)
+
+ self.remove_signals()
+ u = User.objects.create_superuser(username, email, password)
+ u.set_admin_status()
+ u.save()
+
+ if verbosity >= 1:
+ self.stdout.write("Askbot Superuser created successfully.\n")
+
+
+ def remove_signals(self):
+ pre_save.receivers = []
+ post_save.receivers = []
diff --git a/askbot/management/commands/update_avatar_data.py b/askbot/management/commands/update_avatar_data.py
new file mode 100644
index 00000000..dd2749f4
--- /dev/null
+++ b/askbot/management/commands/update_avatar_data.py
@@ -0,0 +1,30 @@
+from django.core.management.base import NoArgsCommand
+from django.contrib.auth.models import User
+from django.db import transaction
+from askbot.utils.console import print_action
+
+class Command(NoArgsCommand):
+ help = 'updates data about currently used avatars, ' + \
+ 'necessary for display of avatars on the front page'
+
+ @transaction.commit_manually
+ def handle_noargs(self, **options):
+ users = User.objects.all()
+ has_avatar = User.objects.exclude(avatar_type='n').count()
+ total_users = users.count()
+ print '%s users in total, %s have valid avatar' \
+ % (total_users, has_avatar)
+
+ for count, user in enumerate(users):
+ users_left = total_users - count
+ print_action(
+ 'Updating %s (%d users left)' % (user.username, users_left)
+ )
+ user.update_avatar_type()
+ transaction.commit()
+
+ print 'Updated all the users'
+ has_avatar = User.objects.exclude(avatar_type='n').count()
+ transaction.commit()
+ print '%s users in total, %s have valid avatar' \
+ % (total_users, has_avatar)
diff --git a/askbot/migrations/0005_install_badges.py b/askbot/migrations/0005_install_badges.py
index 278f1ce1..f3c33fe5 100644
--- a/askbot/migrations/0005_install_badges.py
+++ b/askbot/migrations/0005_install_badges.py
@@ -4,6 +4,7 @@ import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
+from unidecode import unidecode
INITIAL_BADGE_DATA = (
('Disciplined', 3, 'disciplined', 'Deleted own post with score of 3 or higher', True, 0),
@@ -58,9 +59,9 @@ class Migration(DataMigration):
try:
badge = orm.Badge.objects.get(name=name)
- print 'already have badge %s' % name
+ print 'already have badge %s' % unidecode(name)
except orm.Badge.DoesNotExist:
- print 'adding new badge %s' % name
+ print 'adding new badge %s' % unidecode(name)
badge = orm.Badge()
badge.name = name
@@ -79,9 +80,9 @@ class Migration(DataMigration):
badge = orm.Badge.objects.get(name = name)
badge.award_badge.clear()
badge.delete()
- print 'deleted badge %s' % name
+ print 'deleted badge %s' % unidecode(name)
except orm.Badge.DoesNotExist:
- print 'no such badge %s - so skipping' % name
+ print 'no such badge %s - so skipping' % unidecode(name)
pass
forum_app_name = os.path.basename(os.path.dirname(os.path.dirname(__file__)))
diff --git a/askbot/migrations/0006_add_subscription_setting_for_comments_and_mentions.py b/askbot/migrations/0006_add_subscription_setting_for_comments_and_mentions.py
index cf348c22..5d077ebb 100644
--- a/askbot/migrations/0006_add_subscription_setting_for_comments_and_mentions.py
+++ b/askbot/migrations/0006_add_subscription_setting_for_comments_and_mentions.py
@@ -4,6 +4,7 @@ import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
+from unidecode import unidecode
try:
from forum import const
email_feed_setting_model = 'forum.EmailFeedSetting'
@@ -45,7 +46,7 @@ class Migration(DataMigration):
verbose_frequency = dict(const.NOTIFICATION_DELIVERY_SCHEDULE_CHOICES)[frequency]
print 'added \'%s\' subscription for %s (%d)' % (
verbose_frequency,
- user.username,
+ unidecode(user.username),
user.id
)
diff --git a/askbot/migrations/0009_calculate_html_field_for_comments.py b/askbot/migrations/0009_calculate_html_field_for_comments.py
index 53de1832..41535dd6 100644
--- a/askbot/migrations/0009_calculate_html_field_for_comments.py
+++ b/askbot/migrations/0009_calculate_html_field_for_comments.py
@@ -159,8 +159,6 @@ class Migration(DataMigration):
all_users = all_users,
orm = orm
)
- #print 'was %s' % comment.comment
- #print 'now %s' % comment.html
comment.save()
def backwards(self, orm):
diff --git a/askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py b/askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py
index c5213526..5371d7b1 100644
--- a/askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py
+++ b/askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py
@@ -5,6 +5,7 @@ from south.v2 import DataMigration
from django.db import models
from askbot import const
from askbot.migrations_api.version1 import API
+from unidecode import unidecode
#some of activities are not related to question, so they are not processed here
APPROPRIATE_ACTIVITIES = (
@@ -51,7 +52,7 @@ class Migration(DataMigration):
if have_problems:
print 'Migration is now complete, but there were some errors:'
- print '\n'.join(errors)
+ print unidecode('\n'.join(errors))
print 'problematic activity objects are: ' + ','.join(bad_ids)
print 'This is most likely not a big issue, but if you save this error message'
print 'and email to admin@askbot.org, that would help. Thanks.'
diff --git a/askbot/migrations/0043_add_temporal_extra_column_for_datamigration.py b/askbot/migrations/0043_add_temporal_extra_column_for_datamigration.py
new file mode 100644
index 00000000..4a8140a4
--- /dev/null
+++ b/askbot/migrations/0043_add_temporal_extra_column_for_datamigration.py
@@ -0,0 +1,308 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ try:
+ db.add_column(u'auth_user', 'avatar_type', self.gf('django.db.models.fields.CharField')(max_length=1, default='n'), keep_default=False)
+ except:
+ pass
+
+ def backwards(self, orm):
+ db.delete_column(u'auth_user', 'avatar_type')
+
+ models = {
+ 'askbot.activity': {
+ 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
+ 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.anonymousanswer': {
+ 'Meta': {'object_name': 'AnonymousAnswer'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Question']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.anonymousquestion': {
+ 'Meta': {'object_name': 'AnonymousQuestion'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.answer': {
+ 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"},
+ 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['askbot.Question']"}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.answerrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"},
+ 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Answer']"}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.comment': {
+ 'Meta': {'ordering': "('-added_at',)", 'object_name': 'Comment', 'db_table': "u'comment'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'offensive_flag_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.question': {
+ 'Meta': {'object_name': 'Question', 'db_table': "u'question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}),
+ 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'symmetrical': 'False', 'through': "orm['askbot.FavoriteQuestion']", 'to': "orm['auth.User']"}),
+ 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_questions'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'to': "orm['auth.User']"}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.questionrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Question']"}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Question']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'has_custom_avatar': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0044_migrate_has_custom_avatar_field.py b/askbot/migrations/0044_migrate_has_custom_avatar_field.py
new file mode 100644
index 00000000..2222e871
--- /dev/null
+++ b/askbot/migrations/0044_migrate_has_custom_avatar_field.py
@@ -0,0 +1,323 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+from askbot.utils.console import print_action
+from unidecode import unidecode
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ print 'Migrating users to new avatar field'
+ for user in orm['auth.user'].objects.all():
+ print_action('migrating user: %s' % unidecode(user.username))
+ if user.has_custom_avatar == True:
+ user.avatar_type = 'a'
+ else:
+ user.avatar_type = 'n'
+ user.save()
+ print_action(
+ 'user %s migrated avatar_type: %s' % \
+ (unidecode(user.username), user.avatar_type)
+ )
+
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+ pass
+
+
+ models = {
+ 'askbot.activity': {
+ 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
+ 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.anonymousanswer': {
+ 'Meta': {'object_name': 'AnonymousAnswer'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Question']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.anonymousquestion': {
+ 'Meta': {'object_name': 'AnonymousQuestion'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.answer': {
+ 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"},
+ 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['askbot.Question']"}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.answerrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"},
+ 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Answer']"}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.comment': {
+ 'Meta': {'ordering': "('-added_at',)", 'object_name': 'Comment', 'db_table': "u'comment'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'offensive_flag_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.question': {
+ 'Meta': {'object_name': 'Question', 'db_table': "u'question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}),
+ 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'symmetrical': 'False', 'through': "orm['askbot.FavoriteQuestion']", 'to': "orm['auth.User']"}),
+ 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_questions'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'to': "orm['auth.User']"}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.questionrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Question']"}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Question']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'has_custom_avatar': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0045_delete_has_custom_avatar.py b/askbot/migrations/0045_delete_has_custom_avatar.py
new file mode 100644
index 00000000..0653c14f
--- /dev/null
+++ b/askbot/migrations/0045_delete_has_custom_avatar.py
@@ -0,0 +1,310 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.delete_column(u'auth_user', 'has_custom_avatar')
+
+ def backwards(self, orm):
+ try:
+ db.add_column(u'auth_user', 'has_custom_avatar', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
+ except:
+ pass
+
+
+ models = {
+ 'askbot.activity': {
+ 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
+ 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.anonymousanswer': {
+ 'Meta': {'object_name': 'AnonymousAnswer'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Question']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.anonymousquestion': {
+ 'Meta': {'object_name': 'AnonymousQuestion'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.answer': {
+ 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"},
+ 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['askbot.Question']"}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.answerrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"},
+ 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Answer']"}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.comment': {
+ 'Meta': {'ordering': "('-added_at',)", 'object_name': 'Comment', 'db_table': "u'comment'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'offensive_flag_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.question': {
+ 'Meta': {'object_name': 'Question', 'db_table': "u'question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}),
+ 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'symmetrical': 'False', 'through': "orm['askbot.FavoriteQuestion']", 'to': "orm['auth.User']"}),
+ 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_questions'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'to': "orm['auth.User']"}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.questionrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Question']"}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Question']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'has_custom_avatar': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index ce6d4881..85533ece 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -58,7 +58,13 @@ User.add_to_class('reputation',
models.PositiveIntegerField(default=const.MIN_REPUTATION)
)
User.add_to_class('gravatar', models.CharField(max_length=32))
-User.add_to_class('has_custom_avatar', models.BooleanField(default=False))
+#User.add_to_class('has_custom_avatar', models.BooleanField(default=False))
+User.add_to_class(
+ 'avatar_type',
+ models.CharField(max_length=1,
+ choices=const.AVATAR_STATUS_CHOICE,
+ default='n')
+)
User.add_to_class('gold', models.SmallIntegerField(default=0))
User.add_to_class('silver', models.SmallIntegerField(default=0))
User.add_to_class('bronze', models.SmallIntegerField(default=0))
@@ -120,38 +126,51 @@ def user_get_avatar_url(self, size):
it will use avatar provided through that app
"""
if 'avatar' in django_settings.INSTALLED_APPS:
- if self.has_custom_avatar == False:
+ if self.avatar_type == 'n':
import avatar
if avatar.settings.AVATAR_GRAVATAR_BACKUP:
return self.get_gravatar_url(size)
else:
return avatar.utils.get_default_avatar_url()
- kwargs = {'user_id': self.id, 'size': size}
- try:
- return reverse('avatar_render_primary', kwargs = kwargs)
- except NoReverseMatch:
- message = 'Please, make sure that avatar urls are in the urls.py '\
- 'or update your django-avatar app, '\
- 'currently it is impossible to serve avatars.'
- logging.critical(message)
- raise django_exceptions.ImproperlyConfigured(message)
+ elif self.avatar_type == 'a':
+ kwargs = {'user_id': self.id, 'size': size}
+ try:
+ return reverse('avatar_render_primary', kwargs = kwargs)
+ except NoReverseMatch:
+ message = 'Please, make sure that avatar urls are in the urls.py '\
+ 'or update your django-avatar app, '\
+ 'currently it is impossible to serve avatars.'
+ logging.critical(message)
+ raise django_exceptions.ImproperlyConfigured(message)
+ else:
+ return self.get_gravatar_url(size)
else:
return self.get_gravatar_url(size)
-
-def user_update_has_custom_avatar(self):
+def user_update_avatar_type(self):
"""counts number of custom avatars
- and if zero, sets has_custom_avatar to False,
+ and if zero, sets avatar_type to False,
True otherwise. The method is called only if
avatar application is installed.
Saves the object.
"""
- if self.avatar_set.count() > 0:
- self.has_custom_avatar = True
+
+ if 'avatar' in django_settings.INSTALLED_APPS:
+ if self.avatar_set.count() > 0:
+ self.avatar_type = 'a'
+ else:
+ self.avatar_type = _check_gravatar(self.gravatar)
else:
- self.has_custom_avatar = False
+ self.avatar_type = _check_gravatar(self.gravatar)
self.save()
+def _check_gravatar(gravatar):
+ gravatar_url = "http://www.gravatar.com/avatar/%s?d=404" % gravatar
+ code = urllib.urlopen(gravatar_url).getcode()
+ if urllib.urlopen(gravatar_url).getcode() != 404:
+ return 'g' #gravatar
+ else:
+ return 'n' #none
def user_get_old_vote_for_post(self, post):
"""returns previous vote for this post
@@ -1994,7 +2013,10 @@ User.add_to_class(
'add_missing_askbot_subscriptions',
user_add_missing_askbot_subscriptions
)
-User.add_to_class('is_username_taken',classmethod(user_is_username_taken))
+User.add_to_class(
+ 'is_username_taken',
+ classmethod(user_is_username_taken)
+)
User.add_to_class(
'get_followed_question_alert_frequency',
user_get_followed_question_alert_frequency
@@ -2007,7 +2029,7 @@ User.add_to_class('get_absolute_url', user_get_absolute_url)
User.add_to_class('get_avatar_url', user_get_avatar_url)
User.add_to_class('get_gravatar_url', user_get_gravatar_url)
User.add_to_class('get_anonymous_name', user_get_anonymous_name)
-User.add_to_class('update_has_custom_avatar', user_update_has_custom_avatar)
+User.add_to_class('update_avatar_type', user_update_avatar_type)
User.add_to_class('post_question', user_post_question)
User.add_to_class('edit_question', user_edit_question)
User.add_to_class('retag_question', user_retag_question)
@@ -2547,11 +2569,12 @@ def post_anonymous_askbot_content(
they are not used in this function"""
user.post_anonymous_askbot_content(session_key)
-def set_user_has_custom_avatar_flag(instance, created, **kwargs):
- instance.user.update_has_custom_avatar()
+def set_user_avatar_type_flag(instance, created, **kwargs):
+ instance.user.update_avatar_type()
+
+def update_user_avatar_type_flag(instance, **kwargs):
+ instance.user.update_avatar_type()
-def update_user_has_custom_avatar_flag(instance, **kwargs):
- instance.user.update_has_custom_avatar()
def make_admin_if_first_user(instance, **kwargs):
user_count = User.objects.all().count()
@@ -2570,14 +2593,15 @@ django_signals.post_save.connect(
record_favorite_question,
sender=FavoriteQuestion
)
+
if 'avatar' in django_settings.INSTALLED_APPS:
from avatar.models import Avatar
django_signals.post_save.connect(
- set_user_has_custom_avatar_flag,
+ set_user_avatar_type_flag,
sender=Avatar
)
django_signals.post_delete.connect(
- update_user_has_custom_avatar_flag,
+ update_user_avatar_type_flag,
sender=Avatar
)
@@ -2644,5 +2668,3 @@ __all__ = [
'get_model'
]
-
-
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 330d3fdb..0ddb5b08 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -373,7 +373,10 @@ class QuestionQuerySet(models.query.QuerySet):
u_id = u_id.union(
set(Answer.objects.filter(id__in=a_id).values_list('author', flat=True))
)
- contributors = User.objects.filter(id__in=u_id).order_by('?')
+
+ from askbot.conf import settings as askbot_settings
+ avatar_limit = askbot_settings.SIDEBAR_MAIN_AVATAR_LIMIT
+ contributors = User.objects.filter(id__in=u_id).order_by('avatar_type', '?')[:avatar_limit]
#print contributors
#could not optimize this query with indices so it was split into what's now above
#contributors = User.objects.filter(
diff --git a/askbot/schedules.py b/askbot/schedules.py
new file mode 100644
index 00000000..b9bbdbc8
--- /dev/null
+++ b/askbot/schedules.py
@@ -0,0 +1,19 @@
+"""tests on whether certain scheduled tasks need
+to be performed at the moment"""
+from datetime import datetime
+
+def should_update_avatar_data(request):
+ """True if it is time to update user's avatar data
+ user is taken from the request object
+ """
+ user = request.user
+ if user.is_authenticated():
+ if (datetime.today() - user.last_login).days <= 1:
+ #avatar is updated on login anyway
+ return False
+ updated_at = request.session.get('avatar_data_updated_at', None)
+ if updated_at is None:
+ return True
+ else:
+ return (datetime.now() - updated_at).days > 0
+ return False
diff --git a/askbot/skins/default/media/images/cc-by-sa.png b/askbot/skins/default/media/images/cc-by-sa.png
new file mode 100644
index 00000000..f0a944e0
--- /dev/null
+++ b/askbot/skins/default/media/images/cc-by-sa.png
Binary files differ
diff --git a/askbot/skins/default/media/images/cc-wiki.png b/askbot/skins/default/media/images/cc-wiki.png
deleted file mode 100755
index 3e680538..00000000
--- a/askbot/skins/default/media/images/cc-wiki.png
+++ /dev/null
Binary files differ
diff --git a/askbot/skins/default/media/jquery-openid/jquery.openid.js b/askbot/skins/default/media/jquery-openid/jquery.openid.js
index bb76fcd6..7ba9adce 100644
--- a/askbot/skins/default/media/jquery-openid/jquery.openid.js
+++ b/askbot/skins/default/media/jquery-openid/jquery.openid.js
@@ -287,6 +287,9 @@ $.fn.authenticator = function() {
signin_form.submit();
}
else {
+ if (FB.getSession()){
+ signin_form.submit();
+ }
FB.login();
}
});
@@ -295,9 +298,21 @@ $.fn.authenticator = function() {
};
var start_password_login_or_change = function(){
+ //called upon clicking on one of the password login buttons
reset_form();
set_provider_name($(this));
var provider_name = $(this).attr('name');
+ return setup_password_login_or_change(provider_name);
+ };
+
+ var init_always_visible_password_login = function(){
+ reset_form();
+ //will break wordpress and ldap
+ provider_name_input.val('local');
+ setup_password_login_or_change('local');
+ };
+
+ var setup_password_login_or_change = function(provider_name){
var token_name = extra_token_name[provider_name]
var password_action_input = $('input[name=password_action]');
if (userIsAuthenticated === true){
@@ -424,6 +439,9 @@ $.fn.authenticator = function() {
};
setup_default_handlers();
+ if (askbot['settings']['signin_always_show_local_login'] === true){
+ init_always_visible_password_login();
+ }
clear_password_fields();
return this;
};
diff --git a/askbot/skins/default/media/js/post.js b/askbot/skins/default/media/js/post.js
index 8216c10a..52772cc0 100644
--- a/askbot/skins/default/media/js/post.js
+++ b/askbot/skins/default/media/js/post.js
@@ -1040,12 +1040,14 @@ EditCommentForm.prototype.createDom = function(){
this._textarea = $('<textarea></textarea>');
this._textarea.attr('id', this._id);
+ /*
this._help_text = $('<span></span>').attr('class', 'help-text');
this._help_text.html(gettext('Markdown is allowed in the comments'));
div.append(this._help_text);
this._help_text = $('<div></div>').attr('class', 'clearfix');
div.append(this._help_text);
+ */
this._element.append(div);
div.append(this._textarea);
@@ -1270,7 +1272,7 @@ Comment.prototype.setContent = function(data){
this._comment_body = $('<div class="comment-body"></div>');
this._comment_body.html(this._data['html']);
- this._comment_body.append(' &ndash; ');
+ //this._comment_body.append(' &ndash; ');
this._user_link = $('<a></a>').attr('class', 'author');
this._user_link.attr('href', this._data['user_url']);
@@ -1531,7 +1533,7 @@ var socialSharing = function(){
var SERVICE_DATA = {
//url - template for the sharing service url, params are for the popup
identica: {
- url: "http://identi.ca/index.php?action=newnotice&status_textarea={TEXT}",
+ url: "http://identi.ca/notice/new?status_textarea={TEXT}%20{URL}",
params: "width=820, height=526,toolbar=1,status=1,resizable=1,scrollbars=1"
},
twitter: {
@@ -1553,12 +1555,25 @@ var socialSharing = function(){
var share_page = function(service_name){
if (SERVICE_DATA[service_name]){
var url = SERVICE_DATA[service_name]['url'];
- url = url.replace('{URL}', URL);
- url = url.replace('{TEXT}', TEXT);
- var params = SERVICE_DATA[service_name]['params'];
- if(!window.open(url, "sharing", params)){
- window.location.href=share_url;
- }
+ $.ajax({
+ async: false,
+ url: "http://json-tinyurl.appspot.com/?&callback=?",
+ dataType: "json",
+ data: {'url':URL},
+ success: function(data){
+ url = url.replace('{URL}', data.tinyurl);
+ },
+ error: function(data){
+ url = url.replace('{URL}', URL);
+ },
+ complete: function(data){
+ url = url.replace('{TEXT}', TEXT);
+ var params = SERVICE_DATA[service_name]['params'];
+ if(!window.open(url, "sharing", params)){
+ window.location.href=share_url;
+ }
+ }
+ });
}
}
@@ -1572,6 +1587,8 @@ var socialSharing = function(){
var ica = $('a.identica-share');
copyAltToTitle(fb);
copyAltToTitle(tw);
+ copyAltToTitle(ln);
+ copyAltToTitle(ica);
setupButtonEventHandlers(fb, function(){share_page("facebook")});
setupButtonEventHandlers(tw, function(){share_page("twitter")});
setupButtonEventHandlers(ln, function(){share_page("linkedin")});
diff --git a/askbot/skins/default/media/js/utils.js b/askbot/skins/default/media/js/utils.js
index c49da02b..ec55e535 100644
--- a/askbot/skins/default/media/js/utils.js
+++ b/askbot/skins/default/media/js/utils.js
@@ -3,6 +3,11 @@ var mediaUrl = function(resource){
return scriptUrl + 'm/' + askbotSkin + '/' + resource;
};
+var cleanUrl = function(url){
+ var re = new RegExp('//', 'g');
+ return url.replace(re, '/');
+};
+
var copyAltToTitle = function(sel){
sel.attr('title', sel.attr('alt'));
};
diff --git a/askbot/skins/default/media/style/jquery.autocomplete.css b/askbot/skins/default/media/style/jquery.autocomplete.css
index 1ad98ddf..b3d7b759 100644
--- a/askbot/skins/default/media/style/jquery.autocomplete.css
+++ b/askbot/skins/default/media/style/jquery.autocomplete.css
@@ -28,7 +28,7 @@
}
.acLoading {
- background : url('indicator.gif') right center no-repeat;
+ background : url('../images/indicator.gif') right center no-repeat;
}
.acSelect {
diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css
index 7627be5e..1431135f 100644
--- a/askbot/skins/default/media/style/style.css
+++ b/askbot/skins/default/media/style/style.css
@@ -194,6 +194,10 @@ blockquote {
background: #777;
}
+#ground p {
+ margin-bottom:0;
+}
+
#ab-logo {
padding: 0px 0px 0px 10px;
position: absolute;
@@ -203,6 +207,10 @@ blockquote {
width: 70px;
}
+img.license-logo {
+ margin: 6px 0 10px 0;
+}
+
#ab-meta-nav,
#ab-main-nav {
position: absolute;
@@ -1100,6 +1108,12 @@ div.comment .comment-body {
color: #666;
}
+div.comment .comment-body p{
+ font-size:inherit;
+ margin-bottom: 3px;
+ padding: 0;
+}
+
div.comment .comment-delete {
float: right;
width: 14px;
@@ -1147,13 +1161,15 @@ div.comment .upvote:hover {
.comments textarea {
display: block;
- height: 48px;
- width: 560px;
- margin: 3px 0px;
+ height: 42px;
+ width: 572px;
+ margin: 6px 0 5px 1px;
font-family: sans-serif;
+ outline: none;
+ overflow:auto;
font-size: 12px;
- line-height: 130%;
- padding: 4px;
+ line-height: 140%;
+ padding-left:2px;
}
.comments input {
@@ -1184,6 +1200,9 @@ div.comment .upvote:hover {
float: right;
text-align:right;
color: gray;
+ margin-bottom: 0px;
+ margin-top: 0px;
+ line-height: 50%;
}
span.text-counter {
@@ -1900,6 +1919,21 @@ button::-moz-focus-inner {
text-decoration: none;
}
+.noscript {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ z-index: 100;
+ padding: 5px 0;
+ text-align: center;
+ font-family: sans-serif;
+ font-size: 120%;
+ font-weight: Bold;
+ color: #FFFFFF;
+ background-color: #AE0000;
+}
+
.big {
font-size: 15px;
}
diff --git a/askbot/skins/default/templates/authopenid/providers_javascript.html b/askbot/skins/default/templates/authopenid/providers_javascript.html
index 6ef86b29..0fe72eb3 100644
--- a/askbot/skins/default/templates/authopenid/providers_javascript.html
+++ b/askbot/skins/default/templates/authopenid/providers_javascript.html
@@ -34,13 +34,8 @@
{% else %}
var userIsAuthenticated = false;
{% endif %}
- $("body").authenticator();
- {% if settings.SIGNIN_ALWAYS_SHOW_LOCAL_LOGIN %}
- {% if settings.SIGNIN_LOCAL_ENABLED %}
- $('input.password').remove();
- {% endif %}
- {%endif%}
askbot['settings']['signin_always_show_local_login'] = {% if settings.SIGNIN_ALWAYS_SHOW_LOCAL_LOGIN %}true{% else %}false{% endif %};
+ $("body").authenticator();
</script>
{% if settings.FACEBOOK_KEY and settings.FACEBOOK_SECRET %}
<div id="fb-root"></div>
@@ -51,11 +46,8 @@
var ret = FB.init({appId: '{{settings.FACEBOOK_KEY}}', status: true, cookie: true, xfbml: true});
FB.Event.subscribe('auth.sessionChange', function(response){
if (response.session) {
- // A user has logged in, and a new cookie has been saved
$('#signin-form').submit();
- } else {
- // The user has logged out, and the cookie has been cleared
- }
+ }
});
};
});
diff --git a/askbot/skins/default/templates/authopenid/signin.html b/askbot/skins/default/templates/authopenid/signin.html
index 37636207..49c447a1 100644
--- a/askbot/skins/default/templates/authopenid/signin.html
+++ b/askbot/skins/default/templates/authopenid/signin.html
@@ -94,7 +94,7 @@
{% endif %}
<table class="login">
<tr>
- <td><label for="id_username">{% trans %}Login name{% endtrans %}</label></td>
+ <td><label for="id_username">{% trans %}Login or email{% endtrans %}</label></td>
<td>{{login_form.username}}</td>
</tr>
<tr>
diff --git a/askbot/skins/default/templates/base.html b/askbot/skins/default/templates/base.html
index 31abe043..9f779945 100644
--- a/askbot/skins/default/templates/base.html
+++ b/askbot/skins/default/templates/base.html
@@ -60,6 +60,11 @@
{% include "blocks/bottom_scripts.html" %}
{% block endjs %}
{% endblock %}
+ <script type="text/javascript">
+ for (url_name in askbot['urls']){
+ askbot['urls'][url_name] = cleanUrl(askbot['urls'][url_name]);
+ }
+ </script>
</body>
</html>
<!-- end template base.html -->
diff --git a/askbot/skins/default/templates/blocks/bottom_scripts.html b/askbot/skins/default/templates/blocks/bottom_scripts.html
index aaad70c0..771c13a4 100644
--- a/askbot/skins/default/templates/blocks/bottom_scripts.html
+++ b/askbot/skins/default/templates/blocks/bottom_scripts.html
@@ -2,6 +2,11 @@
this template is included at the very bottow of the
main template "base.html"
#}
+<div id="no-javascript">
+ <noscript class="noscript">
+ {% trans app_name = settings.APP_SHORT_NAME %}Please note: {{app_name}} requires javascript to work properly, please enable javascript in your browser, <a href="{{noscript_url}}">here is how</a>{% endtrans %}
+ </noscript>
+</div>
<script type="text/javascript">
var i18nLang = '{{settings.LANGUAGE_CODE}}';
var scriptUrl = '/{{settings.ASKBOT_URL}}'
diff --git a/askbot/skins/default/templates/blocks/footer.html b/askbot/skins/default/templates/blocks/footer.html
index 582e6d33..94c409ef 100644
--- a/askbot/skins/default/templates/blocks/footer.html
+++ b/askbot/skins/default/templates/blocks/footer.html
@@ -1,32 +1,53 @@
<!-- template footer.html -->
<div id="ground">
- <div>
- <div class="footerLinks" >
- <a href="{% url about %}">{% trans %}about{% endtrans %}</a><span class="link-separator"> |</span>
- <a href="{% url faq %}">{% trans %}faq{% endtrans %}</a><span class="link-separator"> |</span>
- <a href="{% url privacy %}">{% trans %}privacy policy{% endtrans %}</a><span class="link-separator"> |</span>
- {% spaceless %}
- <a href=
- {% if settings.FEEDBACK_SITE_URL %}
- "{{settings.FEEDBACK_SITE_URL}}"
- target="_blank">
- {% else %}
- "{% url feedback %}?next={{request.path}}">
- {% endif %}
- {% trans %}give feedback{% endtrans %}
- </a>
- {% endspaceless %}
- </div>
- <p>
- <a href="http://askbot.org" target="_blank">
- powered by ASKBOT version {{settings.ASKBOT_VERSION}}
- </a><br/>{{settings.APP_COPYRIGHT}}
- </p>
- </div>
- <div id="licenseLogo">
- <a href="http://creativecommons.org/licenses/by/3.0/">
- <img src="{{"/images/cc-wiki.png"|media }}" title="Creative Commons: Attribution - Share Alike" alt="cc-wiki" width="50" height="68" />
+ <p class="footerLinks" >
+ <a href="{% url about %}">{% trans %}about{% endtrans %}</a><span class="link-separator"> |</span>
+ <a href="{% url faq %}">{% trans %}faq{% endtrans %}</a><span class="link-separator"> |</span>
+ <a href="{% url privacy %}">{% trans %}privacy policy{% endtrans %}</a><span class="link-separator"> |</span>
+ {% spaceless %}
+ <a href=
+ {% if settings.FEEDBACK_SITE_URL %}
+ "{{settings.FEEDBACK_SITE_URL}}"
+ target="_blank">
+ {% else %}
+ "{% url feedback %}?next={{request.path}}">
+ {% endif %}
+ {% trans %}give feedback{% endtrans %}
+ </a>
+ {% endspaceless %}
+ </p>
+ <p>
+ <a href="http://askbot.org" target="_blank">
+ Powered by Askbot version {{settings.ASKBOT_VERSION}}
</a>
- </div>
+ </p>
+ {% if settings.USE_LICENSE %}{# could be factored out into separate template #}
+ {% if settings.LICENSE_USE_URL %}
+ <p>{{settings.APP_COPYRIGHT}} {% trans
+ license_title=settings.LICENSE_TITLE,
+ license_url=settings.LICENSE_URL
+ %}Content on this site is licensed under a <a href="{{license_url}}">{{license_title}}</a> license.{% endtrans %}</p>
+ {% if settings.LICENSE_USE_LOGO %}
+ <a href="{{ settings.LICENSE_URL}}">
+ <img
+ class="license-logo"
+ src="{{settings.LICENSE_LOGO_URL|media }}"
+ title="{{settings.LICENSE_ACRONYM}}"
+ alt="{{settings.LICENSE_ACRONYM}}"
+ />
+ </a>
+ {% endif %}
+ {% else %}
+ <p>{{settings.APP_COPYRIGHT}} {% trans license=settings.LICENSE_TITLE %}Content on this site is licensed under a {{license}}{% endtrans %}</p>
+ {% if settings.LICENSE_USE_LOGO %}
+ <img
+ class="license-logo"
+ src="{{settings.LICENSE_LOGO_URL|media }}"
+ title="{{settings.LICENSE_ACRONYM}}"
+ alt="{{settings.LICENSE_ACRONYM}}"
+ />
+ {% endif %}
+ {% endif %}
+ {% endif %}
</div>
<!-- end template footer.html -->
diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html
index fa15426f..7ddbd70f 100644
--- a/askbot/skins/default/templates/macros.html
+++ b/askbot/skins/default/templates/macros.html
@@ -22,7 +22,7 @@
{%- macro share(site = None, site_label = None, icon = False) -%}
<a class="{{ site }}-share{% if icon == True %} icon{% endif %}"
- alt="{% trans %}Share this question on {{site}}{% endtrans %}"
+ title="{% trans %}Share this question on {{site}}{% endtrans %}"
>{% if icon == False %}{% if site_label %}{{ site_label }}{% else %}{{ site }}{% endif %}{% endif %}</a>
{%- endmacro -%}
@@ -138,8 +138,7 @@
{%- macro paginator(p, position='left') -%}{# p is paginator context dictionary #}
{% spaceless %}
{% if p.is_paginated %}
- <div class="paginator">
- <div style="float:{{position}}">
+ <div class="paginator" style="float:{{position}}">
{% if p.has_previous %}
<span class="prev"><a href="{{p.base_url}}page={{ p.previous }}{{ p.extend_url }}" title="{% trans %}previous{% endtrans %}">
&laquo; {% trans %}previous{% endtrans %}</a></span>
@@ -169,7 +168,6 @@
<span class="next"><a href="{{p.base_url}}page={{ p.next }}{{ p.extend_url }}" title="{% trans %}next page{% endtrans %}">{% trans %}next page{% endtrans %} &raquo;</a></span>
{% endif %}
</div>
- </div>
{% endif %}
{% endspaceless %}
{%- endmacro -%}
@@ -494,7 +492,7 @@ for the purposes of the AJAX comment editor #}
{% endif %}
</div>
<div class="comment-body">
- {{comment.html}} &ndash;
+ {{comment.html}}
<a
class="author"
href="{{comment.user.get_profile_url()}}"
diff --git a/askbot/skins/default/templates/main_page/javascript.html b/askbot/skins/default/templates/main_page/javascript.html
index 09a5d15b..14dfe3cd 100644
--- a/askbot/skins/default/templates/main_page/javascript.html
+++ b/askbot/skins/default/templates/main_page/javascript.html
@@ -9,10 +9,14 @@
Hilite.exact = false;
Hilite.elementid = "question-list";
Hilite.debug_referrer = location.href;
+ {% if update_avatar_data == True %}
+ var today = new Date();{#add timestamp to prevent browser caching #}
+ $.getJSON('{% url user_update_has_custom_avatar %}?t=' + today.getTime());
+ {% endif %}
});
- askbot['urls']['mark_interesting_tag'] = scriptUrl + '{% trans %}mark-tag/{% endtrans %}{% trans %}interesting/{% endtrans %}';
- askbot['urls']['mark_ignored_tag'] = scriptUrl + '{% trans %}mark-tag/{% endtrans %}{% trans %}ignored/{% endtrans %}';
- askbot['urls']['unmark_tag'] = scriptUrl + '{% trans %}unmark-tag/{% endtrans %}';
+ askbot['urls']['mark_interesting_tag'] = scriptUrl + '{% url mark_interesting_tag %}';
+ askbot['urls']['mark_ignored_tag'] = scriptUrl + '{% url mark_ignored_tag %}';
+ askbot['urls']['unmark_tag'] = scriptUrl + '{% url unmark_tag %}';
askbot['urls']['set_tag_filter_strategy'] = '{% url "set_tag_filter_strategy" %}';
askbot['urls']['questions'] = '{% url "questions" %}';
askbot['urls']['question_url_template'] = scriptUrl + '{% trans %}question/{% endtrans %}{{ "{{QuestionID}}/" }}';
diff --git a/askbot/skins/default/templates/tags.html b/askbot/skins/default/templates/tags.html
index d37cc9e7..7d9026af 100644
--- a/askbot/skins/default/templates/tags.html
+++ b/askbot/skins/default/templates/tags.html
@@ -31,6 +31,7 @@
<span>{% trans %}Nothing found{% endtrans %}</span>
{% endif %}
{% if tags.object_list %}
+ <div class='clearfix'></div>
<ul class='tags'>
{% for tag in tags.object_list %}
<li>
diff --git a/askbot/skins/utils.py b/askbot/skins/utils.py
index 7eaeb304..e7997f7a 100644
--- a/askbot/skins/utils.py
+++ b/askbot/skins/utils.py
@@ -10,6 +10,7 @@ import logging
import urllib
from django.conf import settings as django_settings
from django.utils.datastructures import SortedDict
+from askbot.utils import hasher
class MediaNotFound(Exception):
"""raised when media file is not found"""
@@ -140,10 +141,11 @@ def get_media_url(url, ignore_missing = False):
#determine from which skin take the media file
try:
use_skin = resolve_skin_for_media(media=url, preferred_skin = use_skin)
- except MediaNotFound, e:
- log_message = 'missing media resource %s in skin %s' \
- % (url, use_skin)
- logging.critical(log_message)
+ except MediaNotFound:
+ if ignore_missing == False:
+ log_message = 'missing media resource %s in skin %s' \
+ % (url, use_skin)
+ logging.critical(log_message)
return None
url = use_skin + '/media/' + url
@@ -160,3 +162,33 @@ def get_media_url(url, ignore_missing = False):
#after = datetime.datetime.now()
#print after - before
return url
+
+def update_media_revision(skin = None):
+ """update skin media revision number based on the contents
+ of the skin media directory"""
+ from askbot.conf import settings as askbot_settings
+ resource_revision = askbot_settings.MEDIA_RESOURCE_REVISION
+
+ if skin:
+ if skin in get_skin_choices():
+ skin_path = get_path_to_skin(skin)
+ else:
+ raise MediaNotFound('Skin %s not found' % skin)
+ else:
+ skin = 'default'
+ skin_path = get_path_to_skin(askbot_settings.ASKBOT_DEFAULT_SKIN)
+
+ media_dirs = [os.path.join(skin_path, 'media'),]
+
+ if skin != 'default':
+ #we have default skin as parent of the custom skin
+ default_skin_path = get_path_to_skin('default')
+ media_dirs.append(os.path.join(default_skin_path, 'media'))
+
+ current_hash = hasher.get_hash_of_dirs(media_dirs)
+
+ if current_hash != askbot_settings.MEDIA_RESOURCE_REVISION_HASH:
+ askbot_settings.update('MEDIA_RESOURCE_REVISION', resource_revision + 1)
+ askbot_settings.update('MEDIA_RESOURCE_REVISION_HASH', current_hash)
+ logging.debug('MEDIA_RESOURCE_REVISION changed')
+ askbot_settings.MEDIA_RESOURCE_REVISION
diff --git a/askbot/startup_procedures.py b/askbot/startup_procedures.py
index bb269600..cc7f86c0 100644
--- a/askbot/startup_procedures.py
+++ b/askbot/startup_procedures.py
@@ -22,6 +22,7 @@ PREAMBLE = """\n
************************"""
def askbot_warning(line):
+ """prints a warning with the nice header, but does not quit"""
print >> sys.stderr, PREAMBLE + '\n' + line
def format_as_text_tuple_entries(items):
@@ -81,7 +82,9 @@ def test_middleware():
'askbot.middleware.view_log.ViewLogMiddleware',
)
if 'debug_toolbar' in django_settings.INSTALLED_APPS:
- required_middleware += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
+ required_middleware += (
+ 'debug_toolbar.middleware.DebugToolbarMiddleware',
+ )
installed_middleware_set = set(django_settings.MIDDLEWARE_CLASSES)
missing_middleware_set = set(required_middleware) - installed_middleware_set
@@ -101,7 +104,8 @@ https://github.com/ASKBOT/askbot-devel/blob/master/askbot/setup_templates/settin
)
#'debug_toolbar.middleware.DebugToolbarMiddleware',
- remove_middleware_set = set(canceled_middleware) & installed_middleware_set
+ remove_middleware_set = set(canceled_middleware) \
+ & installed_middleware_set
if remove_middleware_set:
error_message = """\n\nPlease remove the following middleware entries from
the list of MIDDLEWARE_CLASSES in your settings.py - these are not used any more:\n\n"""
@@ -111,6 +115,7 @@ the list of MIDDLEWARE_CLASSES in your settings.py - these are not used any more
def test_i18n():
+ """askbot requires use of USE_I18N setting"""
if getattr(django_settings, 'USE_I18N', False) == False:
raise ImproperlyConfigured(
'Please set USE_I18N = True in settings.py and '
@@ -119,26 +124,32 @@ def test_i18n():
)
def try_import(module_name, pypi_package_name):
+ """tries importing a module and advises to install
+ A corresponding Python package in the case import fails"""
try:
load_module(module_name)
- except ImportError, e:
- message = unicode(e) + ' run\npip install %s' % pypi_package_name
+ except ImportError, error:
+ message = unicode(error) + ' run\npip install %s' % pypi_package_name
message += '\nTo install all the dependencies at once, type:'
message += '\npip install -r askbot_requirements.txt\n'
raise ImproperlyConfigured(message)
def test_modules():
+ """tests presence of required modules"""
+ try_import('akismet', 'akismet')
try_import('recaptcha_works', 'django-recaptcha-works')
def test_postgres():
- '''Validates postgres buggy driver 2.4.2'''
+ """Checks for the postgres buggy driver, version 2.4.2"""
if hasattr(django_settings, 'DATABASE_ENGINE'):
if django_settings.DATABASE_ENGINE in ('postgresql_psycopg2',):
try:
import psycopg2
version = psycopg2.__version__.split(' ')[0].split('.')
if version == ['2', '4', '2']:
- raise ImproperlyConfigured('Please install psycopg2 version 2.4.1,\n version 2.4.2 has a bug')
+ raise ImproperlyConfigured(
+ 'Please install psycopg2 version 2.4.1,\n version 2.4.2 has a bug'
+ )
elif version > ['2', '4', '2']:
pass #don't know what to do
else:
@@ -181,5 +192,6 @@ def run():
try:
badges.init_badges()
transaction.commit()
- except:
+ except Exception, error:
+ print error
transaction.rollback()
diff --git a/askbot/urls.py b/askbot/urls.py
index c6d79492..54c50d96 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -10,8 +10,11 @@ from django.utils.translation import ugettext as _
from askbot import views
from askbot.feed import RssLastestQuestionsFeed
from askbot.sitemap import QuestionsSitemap
+from askbot.skins.utils import update_media_revision
admin.autodiscover()
+update_media_revision()#needs to be run once, so put it here
+
feeds = {
'rss': RssLastestQuestionsFeed
}
@@ -205,6 +208,11 @@ urlpatterns = patterns('',
name='user_profile'
),
url(
+ r'^%s$' % _('users/update_has_custom_avatar/'),
+ views.users.update_has_custom_avatar,
+ name='user_update_has_custom_avatar'
+ ),
+ url(
r'^%s$' % _('badges/'),
views.meta.badges,
name='badges'
diff --git a/askbot/utils/console.py b/askbot/utils/console.py
index 470856b5..496bbd65 100644
--- a/askbot/utils/console.py
+++ b/askbot/utils/console.py
@@ -72,5 +72,5 @@ def print_progress(elapsed, total, nowipe = False):
operation, in percent, to the console and clear the output with
a backspace character to have the number increment
in-place"""
- output = '%6.2f%%' % 100 * float(elapsed)/float(total)
+ output = '%6.2f%%' % (100 * float(elapsed)/float(total))
print_action(output, nowipe)
diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py
index f2c86cd5..29e92645 100644
--- a/askbot/utils/decorators.py
+++ b/askbot/utils/decorators.py
@@ -7,14 +7,17 @@ import inspect
import logging
from django.conf import settings
from django.core import exceptions as django_exceptions
-from django.core import urlresolvers
+from django.core.urlresolvers import reverse
+from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponse, HttpResponseForbidden, Http404
from django.http import HttpResponseRedirect
from django.utils import simplejson
from django.utils.translation import ugettext as _
+from django.utils.encoding import smart_str
from askbot import exceptions as askbot_exceptions
from askbot.conf import settings as askbot_settings
from askbot.utils import url_utils
+from askbot import get_version
def auto_now_timestamp(func):
"""decorator that will automatically set
@@ -33,12 +36,12 @@ def auto_now_timestamp(func):
def ajax_login_required(view_func):
@functools.wraps(view_func)
- def wrap(request,*args,**kwargs):
+ def wrap(request, *args, **kwargs):
if request.user.is_authenticated():
- return view_func(request,*args,**kwargs)
+ return view_func(request, *args, **kwargs)
else:
json = simplejson.dumps({'login_required':True})
- return HttpResponseForbidden(json,mimetype='application/json')
+ return HttpResponseForbidden(json, mimetype='application/json')
return wrap
@@ -74,14 +77,13 @@ def post_only(view_func):
return view_func(request, *args, **kwargs)
return wrapper
-
def ajax_only(view_func):
@functools.wraps(view_func)
- def wrapper(request,*args,**kwargs):
+ def wrapper(request, *args, **kwargs):
if not request.is_ajax():
raise Http404
try:
- data = view_func(request,*args,**kwargs)
+ data = view_func(request, *args, **kwargs)
except Exception, e:
message = unicode(e)
if message == '':
@@ -99,7 +101,7 @@ def ajax_only(view_func):
else:
data['success'] = 1
json = simplejson.dumps(data)
- return HttpResponse(json,mimetype='application/json')
+ return HttpResponse(json, mimetype='application/json')
return wrapper
def check_authorization_to_post(func_or_message):
@@ -166,3 +168,53 @@ def profile(log_file):
return _inner
return _outer
+
+def check_spam(field):
+ '''Decorator to check if there is spam in the form'''
+
+ def decorator(view_func):
+ @functools.wraps(view_func)
+ def wrapper(request, *args, **kwargs):
+
+ if askbot_settings.USE_AKISMET and askbot_settings.AKISMET_API_KEY == "":
+ raise ImproperlyConfigured('You have not set AKISMET_API_KEY')
+
+ if askbot_settings.USE_AKISMET and request.method == "POST":
+ comment = smart_str(request.POST[field])
+ data = {'user_ip': request.META["REMOTE_ADDR"],
+ 'user_agent': request.environ['HTTP_USER_AGENT'],
+ 'comment_author': smart_str(request.user.username),
+ }
+ if request.user.is_authenticated():
+ data.update({'comment_author_email': request.user.email})
+
+ from akismet import Akismet
+ api = Akismet(
+ askbot_settings.AKISMET_API_KEY,
+ smart_str(askbot_settings.APP_URL),
+ "Askbot/%s" % get_version()
+ )
+
+ if api.comment_check(comment, data, build_data=False):
+ logging.debug(
+ 'Spam detected in %s post at: %s',
+ request.user.username,
+ datetime.datetime.now()
+ )
+ spam_message = _(
+ 'Spam was detected on your post, sorry '
+ 'for if this is a mistake'
+ )
+ if request.is_ajax():
+ return HttpResponseForbidden(
+ spam_message,
+ mimetype="application/json"
+ )
+ else:
+ request.user.message_set.create(message=spam_message)
+ return HttpResponseRedirect(reverse('index'))
+
+ return view_func(request, *args, **kwargs)
+ return wrapper
+
+ return decorator
diff --git a/askbot/utils/functions.py b/askbot/utils/functions.py
index a56ed897..d31d9027 100644
--- a/askbot/utils/functions.py
+++ b/askbot/utils/functions.py
@@ -2,12 +2,13 @@ import re
import datetime
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
+from django.contrib.auth.models import User
def get_from_dict_or_object(source, key):
try:
return source[key]
except:
- return getattr(source,key)
+ return getattr(source, key)
def is_iterable(thing):
@@ -53,7 +54,7 @@ def not_a_robot_request(request):
return False
-def diff_date(date, limen=2, use_on_prefix = False):
+def diff_date(date, use_on_prefix = False):
now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#???
diff = now - date
days = diff.days
@@ -74,9 +75,17 @@ def diff_date(date, limen=2, use_on_prefix = False):
elif days == 1:
return _('yesterday')
elif minutes >= 60:
- return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours}
+ return ungettext(
+ '%(hr)d hour ago',
+ '%(hr)d hours ago',
+ hours
+ ) % {'hr':hours}
else:
- return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes}
+ return ungettext(
+ '%(min)d min ago',
+ '%(min)d mins ago',
+ minutes
+ ) % {'min':minutes}
#todo: this function may need to be removed to simplify the paginator functionality
LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5
@@ -126,3 +135,10 @@ def setup_paginator(context):
"pages_outside_trailing_range": pages_outside_trailing_range,
"extend_url" : extend_url
}
+
+def get_admin():
+ '''Returns an admin users, usefull for raising flags'''
+ try:
+ return User.objects.filter(is_superuser=True)[0]
+ except:
+ raise Exception('there is no admin users')
diff --git a/askbot/utils/hasher.py b/askbot/utils/hasher.py
new file mode 100644
index 00000000..5a73213d
--- /dev/null
+++ b/askbot/utils/hasher.py
@@ -0,0 +1,43 @@
+"""hasher function that will calculate sha1 hash
+directory contents
+"""
+import hashlib, os
+import logging
+
+def get_hash_of_dirs(dirs):
+ """Hasher function for a directory and its files"""
+ sha_hash = hashlib.sha1()
+ for directory in dirs:
+ if not os.path.exists (directory):
+ return -1
+
+ try:
+ for root, dirs, files in os.walk(directory):
+ for names in files:
+ filepath = os.path.join(root, names)
+ try:
+ file_obj = open(filepath, 'rb')
+ except Exception, error:
+ # You can't open the file for some reason
+ logging.critical(
+ 'cannot open file %s: %s',
+ filepath,
+ error
+ )
+ file_obj.close()
+ continue
+
+ while 1:
+ # Read file in as little chunks
+ buf = file_obj.read(4096)
+ if not buf : break
+ sha_hash.update(hashlib.sha1(buf).hexdigest())
+ file_obj.close()
+
+ except Exception:
+ import traceback
+ # Print the stack traceback
+ traceback.print_exc()
+ return -2
+
+ return sha_hash.hexdigest()
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 20d13adc..ab4ab87b 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -29,6 +29,7 @@ from askbot import exceptions
from askbot.utils.diff import textDiff as htmldiff
from askbot.forms import AdvancedSearchForm, AnswerForm, ShowQuestionForm
from askbot import models
+from askbot import schedules
from askbot.models.badges import award_badges_signal
from askbot import const
from askbot.utils import functions
@@ -304,6 +305,7 @@ def questions(request):
'tag_list_type' : tag_list_type,
'font_size' : font_size,
'tag_filter_strategy_choices': const.TAG_FILTER_STRATEGY_CHOICES,
+ 'update_avatar_data': schedules.should_update_avatar_data(request),
}
assert(request.is_ajax() == False)
diff --git a/askbot/views/users.py b/askbot/views/users.py
index acf1d38f..f1f29f71 100644
--- a/askbot/views/users.py
+++ b/askbot/views/users.py
@@ -17,9 +17,10 @@ from django.core.paginator import Paginator, EmptyPage, InvalidPage
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseForbidden
from django.http import HttpResponseRedirect, Http404
from django.utils.translation import ugettext as _
+from django.utils import simplejson
from django.views.decorators import csrf
from askbot.utils.slug import slugify
from askbot.utils.html import sanitize_html
@@ -1007,3 +1008,13 @@ def user(request, id, slug=None, tab_name=None):
'user_follow_feature_on': ('followit' in django_settings.INSTALLED_APPS),
}
return user_view_func(request, profile_owner, context)
+
+def update_has_custom_avatar(request):
+ """updates current avatar type data for the user
+ """
+ if request.is_ajax() and request.user.is_authenticated():
+ if request.user.avatar_type in ('n', 'g'):
+ request.user.update_avatar_type()
+ request.session['avatar_data_updated_at'] = datetime.datetime.now()
+ return HttpResponse(simplejson.dumps({'status':'ok'}), mimetype='application/json')
+ return HttpResponseForbidden()
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index a2540a90..fcc98761 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -189,6 +189,7 @@ def import_data(request):
#@login_required #actually you can post anonymously, but then must register
@csrf.csrf_protect
@decorators.check_authorization_to_post(_('Please log in to ask questions'))
+@decorators.check_spam('text')
def ask(request):#view used to ask a new question
"""a view to ask a new question
gives space for q title, body, tags and checkbox for to post as wiki
@@ -240,6 +241,18 @@ def ask(request):#view used to ask a new question
)
question.save()
return HttpResponseRedirect(url_utils.get_login_url())
+ else:
+ form = forms.AskForm(request.POST)
+ if 'title' in request.GET:
+ #normally this title is inherited from search query
+ #but it is possible to ask with a parameter title in the url query
+ form.initial['title'] = request.GET['title']
+ else:
+ #attempt to extract title from previous search query
+ search_state = request.session.get('search_state', None)
+ if search_state:
+ query = search_state.query
+ form.initial['title'] = query
else:
#this branch is for the initial load of ask form
form = forms.AskForm()
@@ -249,7 +262,7 @@ def ask(request):#view used to ask a new question
form.initial['title'] = request.GET['title']
else:
#attempt to extract title from previous search query
- search_state = request.session.get('search_state',None)
+ search_state = request.session.get('search_state', None)
if search_state:
query = search_state.query
form.initial['title'] = query
@@ -319,6 +332,7 @@ def retag_question(request, id):
@login_required
@csrf.csrf_protect
+@decorators.check_spam('text')
def edit_question(request, id):
"""edit question view
"""
@@ -406,6 +420,7 @@ def edit_question(request, id):
@login_required
@csrf.csrf_protect
+@decorators.check_spam('text')
def edit_answer(request, id):
answer = get_object_or_404(models.Answer, id=id)
try:
@@ -464,6 +479,7 @@ def edit_answer(request, id):
#todo: rename this function to post_new_answer
@decorators.check_authorization_to_post(_('Please log in to answer questions'))
+@decorators.check_spam('text')
def answer(request, id):#process a new answer
"""view that posts new answer
@@ -548,6 +564,7 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po
data = simplejson.dumps(json_comments)
return HttpResponse(data, mimetype="application/json")
+@decorators.check_spam('comment')
def post_comments(request):#generic ajax handler to load comments to an object
# only support get post comments by ajax now
user = request.user
@@ -587,6 +604,7 @@ def post_comments(request):#generic ajax handler to load comments to an object
raise Http404
@decorators.ajax_only
+@decorators.check_spam('text')
def edit_comment(request):
if request.user.is_authenticated():
comment_id = int(request.POST['comment_id'])
@@ -609,7 +627,7 @@ def edit_comment(request):
'user_id': comment.user.id,
'is_deletable': is_deletable,
'is_editable': is_editable,
- 'score': comment.score,
+ 'score': comment.score,
'voted': comment.is_upvoted_by(request.user),
}
else:
diff --git a/askbot_requirements.txt b/askbot_requirements.txt
index 70bc5874..a1d3b603 100644
--- a/askbot_requirements.txt
+++ b/askbot_requirements.txt
@@ -1,3 +1,4 @@
+akismet
django>=1.1.2
Jinja2
Coffin>=0.3
diff --git a/setup.py b/setup.py
index d319d761..71ed2abb 100644
--- a/setup.py
+++ b/setup.py
@@ -7,6 +7,7 @@ import sys
#you might want to install django-debug-toolbar as well
install_requires = [
+ 'akismet',
'django>=1.1.2',
'Jinja2',
'Coffin>=0.3',