summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/__init__.py4
-rw-r--r--askbot/conf/external_keys.py2
-rw-r--r--askbot/conf/forum_data_rules.py12
-rw-r--r--askbot/conf/login_providers.py60
-rw-r--r--askbot/conf/minimum_reputation.py10
-rw-r--r--askbot/conf/site_settings.py30
-rw-r--r--askbot/conf/skin_general_settings.py28
-rw-r--r--askbot/conf/user_settings.py19
-rw-r--r--askbot/const/__init__.py5
-rw-r--r--askbot/deps/django_authopenid/backends.py34
-rw-r--r--askbot/deps/django_authopenid/forms.py28
-rw-r--r--askbot/deps/django_authopenid/urls.py5
-rw-r--r--askbot/deps/django_authopenid/util.py66
-rw-r--r--askbot/deps/django_authopenid/views.py48
-rw-r--r--askbot/deps/group_messaging/urls.py7
-rw-r--r--askbot/deps/group_messaging/views.py4
-rw-r--r--askbot/deps/livesettings/urls.py5
-rw-r--r--askbot/doc/source/askbot/layout.html6
-rw-r--r--askbot/doc/source/changelog.rst6
-rw-r--r--askbot/doc/source/contributors.rst5
-rw-r--r--askbot/doc/source/index.rst5
-rw-r--r--askbot/doc/source/localization.rst81
-rw-r--r--askbot/doc/source/multilingual.rst46
-rw-r--r--askbot/doc/source/solr.rst80
-rw-r--r--askbot/doc/source/text-search.rst48
-rw-r--r--askbot/feed.py4
-rw-r--r--askbot/forms.py93
-rw-r--r--askbot/importers/stackexchange/management/commands/load_stackexchange.py2
-rw-r--r--askbot/locale/fr/LC_MESSAGES/django.po52
-rw-r--r--askbot/locale/zh_CN/LC_MESSAGES/django.mobin142943 -> 143134 bytes
-rw-r--r--askbot/locale/zh_CN/LC_MESSAGES/django.po3
-rw-r--r--askbot/management/commands/add_admin.py3
-rw-r--r--askbot/management/commands/askbot_add_osqa_content.py5
-rw-r--r--askbot/management/commands/askbot_add_test_content.py38
-rw-r--r--askbot/management/commands/askbot_add_xml_content.py4
-rw-r--r--askbot/management/commands/create_tag_synonyms.py54
-rw-r--r--askbot/management/commands/create_thousand_tags.py8
-rw-r--r--askbot/management/commands/createsuperuser.py3
-rw-r--r--askbot/management/commands/fix_question_tags.py32
-rw-r--r--askbot/management/commands/generate_post_snippets.py3
-rw-r--r--askbot/management/commands/remove_admin.py3
-rw-r--r--askbot/management/commands/rename_tags.py25
-rw-r--r--askbot/management/commands/rename_tags_id.py18
-rw-r--r--askbot/management/commands/send_email_alerts.py27
-rw-r--r--askbot/media/jquery-openid/images/mozilla-persona.gifbin0 -> 2197 bytes
-rw-r--r--askbot/media/jquery-openid/jquery.openid.js36
-rw-r--r--askbot/media/js/group_messaging.js4
-rw-r--r--askbot/media/js/less.min.js32
-rw-r--r--askbot/media/js/post.js2
-rw-r--r--askbot/media/js/utils.js22
-rw-r--r--askbot/media/style/style.css84
-rw-r--r--askbot/media/style/style.less105
-rw-r--r--askbot/middleware/remote_ip.py21
-rw-r--r--askbot/migrations/0055_auto__chg_field_question_thread.py2
-rw-r--r--askbot/migrations/0080_transplant_favquestions_2.py2
-rw-r--r--askbot/migrations/0093_auto__del_field_vote_content_type__del_field_vote_object_id__chg_field.py2
-rw-r--r--askbot/migrations/0099_auto__del_field_thread_accepted_answer__del_field_anonymousanswer_ques.py4
-rw-r--r--askbot/migrations/0172_sync_thread_deleted.py419
-rw-r--r--askbot/migrations/0173_auto__del_unique_tag_name__chg_field_thread_added_at.py426
-rw-r--r--askbot/migrations/0174_auto__add_field_tag_language_code.py423
-rw-r--r--askbot/migrations/0175_auto__add_unique_tag_name_language_code.py421
-rw-r--r--askbot/migrations/0176_populate_tag_language_code.py464
-rw-r--r--askbot/migrations/0177_auto__add_field_tagsynonym_language_code.py424
-rw-r--r--askbot/models/__init__.py122
-rw-r--r--askbot/models/post.py7
-rw-r--r--askbot/models/question.py112
-rw-r--r--askbot/models/signals.py1
-rw-r--r--askbot/models/tag.py50
-rw-r--r--askbot/models/user.py20
-rw-r--r--askbot/patches/__init__.py8
-rw-r--r--askbot/patches/django_patches.py30
-rw-r--r--askbot/schedules.py19
-rw-r--r--askbot/setup_templates/urls.py12
-rw-r--r--askbot/templates/answer_edit.html7
-rw-r--r--askbot/templates/authopenid/complete.html6
-rw-r--r--askbot/templates/authopenid/providers_javascript.html5
-rw-r--r--askbot/templates/authopenid/signin.html1
-rw-r--r--askbot/templates/authopenid/signup_with_password.html5
-rw-r--r--askbot/templates/authopenid/verify_email.html2
-rw-r--r--askbot/templates/close.html4
-rw-r--r--askbot/templates/email/change_settings_info.html3
-rw-r--r--askbot/templates/email/macros.html60
-rw-r--r--askbot/templates/embed/widget_form.html2
-rw-r--r--askbot/templates/feedback.html11
-rw-r--r--askbot/templates/macros.html14
-rw-r--r--askbot/templates/main_page/javascript.html4
-rw-r--r--askbot/templates/meta/bottom_scripts.html1
-rw-r--r--askbot/templates/meta/fonts.html4
-rw-r--r--askbot/templates/meta/html_head_javascript.html2
-rw-r--r--askbot/templates/question/answer_comments.html3
-rw-r--r--askbot/templates/question/new_answer_form.html4
-rw-r--r--askbot/templates/question/question_card.html2
-rw-r--r--askbot/templates/question/question_comments.html3
-rw-r--r--askbot/templates/question_edit.html6
-rw-r--r--askbot/templates/question_retag.html4
-rw-r--r--askbot/templates/reopen.html4
-rw-r--r--askbot/templates/tags/form_bulk_tag_subscription.html2
-rw-r--r--askbot/templates/user_profile/user.html1
-rw-r--r--askbot/templates/user_profile/user_edit.html4
-rw-r--r--askbot/templates/user_profile/user_email_subscriptions.html25
-rw-r--r--askbot/templates/user_profile/user_info.html2
-rw-r--r--askbot/templates/user_profile/user_moderate.html8
-rw-r--r--askbot/templates/user_profile/user_stats.html2
-rw-r--r--askbot/templates/widgets/ask_form.html10
-rw-r--r--askbot/templates/widgets/logo.html8
-rw-r--r--askbot/templates/widgets/question_edit_tips.html2
-rw-r--r--askbot/templates/widgets/question_summary.html9
-rw-r--r--askbot/templatetags/extra_tags.py3
-rw-r--r--askbot/tests/user_model_tests.py3
-rw-r--r--askbot/tests/utils.py2
-rw-r--r--askbot/urls.py14
-rw-r--r--askbot/utils/console.py2
-rw-r--r--askbot/utils/decorators.py6
-rw-r--r--askbot/utils/html.py4
-rw-r--r--askbot/utils/pluralization.py4
-rw-r--r--askbot/utils/translation.py3
-rw-r--r--askbot/utils/url_utils.py5
-rw-r--r--askbot/views/api_v1.py10
-rw-r--r--askbot/views/commands.py55
-rw-r--r--askbot/views/error.py18
-rw-r--r--askbot/views/meta.py42
-rw-r--r--askbot/views/readers.py11
-rw-r--r--askbot/views/users.py19
-rw-r--r--askbot/views/widgets.py4
-rw-r--r--askbot/views/writers.py70
-rw-r--r--askbot_requirements.txt4
-rw-r--r--askbot_requirements_dev.txt4
127 files changed, 4209 insertions, 599 deletions
diff --git a/askbot/__init__.py b/askbot/__init__.py
index 2ac9b6fb..0e4ba506 100644
--- a/askbot/__init__.py
+++ b/askbot/__init__.py
@@ -13,7 +13,7 @@ VERSION = (0, 7, 49)
#values - the package qualifier to use for pip
REQUIREMENTS = {
'akismet': 'akismet',
- 'django': 'django>=1.3.1,<=1.5',
+ 'django': 'django>=1.3.1,<1.6',
'compressor': 'django-compressor==1.2',
'jinja2': 'Jinja2',
'coffin': 'Coffin>=0.3',
@@ -27,7 +27,7 @@ REQUIREMENTS = {
'sanction': 'sanction==0.3.1',
'unidecode': 'unidecode',
'django_countries': 'django-countries==1.0.5',
- 'djcelery': 'django-celery==3.0.11',
+ 'djcelery': 'django-celery>=3.0.11',
'djkombu': 'django-kombu==0.9.4',
'followit': 'django-followit',
'recaptcha_works': 'django-recaptcha-works',
diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py
index 3837b7ae..989cbf46 100644
--- a/askbot/conf/external_keys.py
+++ b/askbot/conf/external_keys.py
@@ -53,8 +53,6 @@ settings.register(
)
)
-
-
settings.register(
livesettings.StringValue(
EXTERNAL_KEYS,
diff --git a/askbot/conf/forum_data_rules.py b/askbot/conf/forum_data_rules.py
index caa93563..cd138a88 100644
--- a/askbot/conf/forum_data_rules.py
+++ b/askbot/conf/forum_data_rules.py
@@ -84,7 +84,7 @@ settings.register(
FORUM_DATA_RULES,
'ALLOW_ASK_ANONYMOUSLY',
default=True,
- description=_('Allow asking questions anonymously'),
+ description=_('Allow logged in users ask anonymously'),
help_text=_(
'Users do not accrue reputation for anonymous questions '
'and their identity is not revealed until they change their '
@@ -96,6 +96,16 @@ settings.register(
settings.register(
livesettings.BooleanValue(
FORUM_DATA_RULES,
+ 'ALLOW_ASK_UNREGISTERED',
+ default=False,
+ description=_('Allow asking without registration'),
+ help_text=_('Enabling ReCaptcha is recommended with this feature')
+ )
+)
+
+settings.register(
+ livesettings.BooleanValue(
+ FORUM_DATA_RULES,
'ALLOW_POSTING_BEFORE_LOGGING_IN',
default = True,
description = _('Allow posting before logging in'),
diff --git a/askbot/conf/login_providers.py b/askbot/conf/login_providers.py
index 36f71502..beb56a72 100644
--- a/askbot/conf/login_providers.py
+++ b/askbot/conf/login_providers.py
@@ -23,6 +23,8 @@ settings.register(
)
)
+#todo: remove this - we don't want the local login button
+#but instead always show the login/password field when used
settings.register(
livesettings.BooleanValue(
LOGIN_PROVIDERS,
@@ -62,6 +64,59 @@ settings.register(
)
)
+settings.register(
+ livesettings.BooleanValue(
+ LOGIN_PROVIDERS,
+ 'SIGNIN_CUSTOM_OPENID_ENABLED',
+ default=False,
+ description=_('Enable custom OpenID login')
+ )
+)
+
+settings.register(
+ livesettings.StringValue(
+ LOGIN_PROVIDERS,
+ 'SIGNIN_CUSTOM_OPENID_NAME',
+ default=_('Custom OpenID'),
+ description=_('Short name for the custom OpenID provider')
+ )
+)
+
+CUSTOM_OPENID_MODE_CHOICES = (
+ ('openid-direct', _('Direct button login')),
+ ('openid-username', _('Requires username'))
+)
+
+settings.register(
+ livesettings.StringValue(
+ LOGIN_PROVIDERS,
+ 'SIGNIN_CUSTOM_OPENID_MODE',
+ default='openid-direct',
+ description=_('Type of OpenID login'),
+ choices=CUSTOM_OPENID_MODE_CHOICES
+ )
+)
+
+settings.register(
+ livesettings.ImageValue(
+ LOGIN_PROVIDERS,
+ 'SIGNIN_CUSTOM_OPENID_LOGIN_BUTTON',
+ default='/images/logo.gif',
+ description=_('Upload custom OpenID icon'),
+ url_resolver=skin_utils.get_media_url
+ )
+)
+
+settings.register(
+ livesettings.StringValue(
+ LOGIN_PROVIDERS,
+ 'SIGNIN_CUSTOM_OPENID_ENDPOINT',
+ default='http://example.com',
+ description=_('Custom OpenID endpoint'),
+ help_text=_('Important: with the "username" mode must have a %%(username)s placeholder e.g. http://example.com/%%(username)s/'),
+ )
+)
+
providers = (
'local',
'AOL',
@@ -70,6 +125,7 @@ providers = (
'Facebook',
'Flickr',
'Google',
+ 'Mozilla Persona',
'Twitter',
'LinkedIn',
'LiveJournal',
@@ -81,7 +137,7 @@ providers = (
'Verisign',
'Yahoo',
'identi.ca',
- 'LaunchPad'
+ 'LaunchPad',
)
DISABLED_BY_DEFAULT = ('LaunchPad',)
@@ -105,7 +161,7 @@ for provider in providers:
'in the "External keys" section'
) % {'provider': provider}
- setting_name = 'SIGNIN_%s_ENABLED' % provider.upper()
+ setting_name = 'SIGNIN_%s_ENABLED' % provider.upper().replace(' ', '_')
settings.register(
livesettings.BooleanValue(
LOGIN_PROVIDERS,
diff --git a/askbot/conf/minimum_reputation.py b/askbot/conf/minimum_reputation.py
index 58b41c18..d0db5891 100644
--- a/askbot/conf/minimum_reputation.py
+++ b/askbot/conf/minimum_reputation.py
@@ -17,6 +17,16 @@ MIN_REP = livesettings.ConfigurationGroup(
settings.register(
livesettings.IntegerValue(
MIN_REP,
+ 'MIN_REP_TO_AUTOAPPROVE_USER',
+ default=10,
+ description=_('Become approved'),
+ help_text=_('Approved users bypass moderation and skip recaptcha')
+ )
+)
+
+settings.register(
+ livesettings.IntegerValue(
+ MIN_REP,
'MIN_REP_TO_VOTE_UP',
default=5,
description=_('Upvote')
diff --git a/askbot/conf/site_settings.py b/askbot/conf/site_settings.py
index 805cc5dc..0ac5b081 100644
--- a/askbot/conf/site_settings.py
+++ b/askbot/conf/site_settings.py
@@ -7,8 +7,11 @@ from askbot.conf.super_groups import CONTENT_AND_UI
from askbot.deps import livesettings
from django.utils.translation import ugettext_lazy as _
from django.conf import settings as django_settings
+from django.core.validators import ValidationError, validate_email
+import re
from urlparse import urlparse
+
QA_SITE_SETTINGS = livesettings.ConfigurationGroup(
'QA_SITE_SETTINGS',
_('URLS, keywords & greetings'),
@@ -132,3 +135,30 @@ settings.register(
)
)
)
+
+def feedback_emails_callback(old_value, new_value):
+ """validates the fedback emails list"""
+ emails = []
+ for value in re.split('\s*,\s*', new_value):
+ if not value:
+ continue
+ try:
+ validate_email(value)
+ emails.append(value)
+ except ValidationError:
+ raise ValueError(
+ _("'%(value)s' is not a valid email") % {'value': value})
+ return ", ".join(emails)
+
+settings.register(
+ livesettings.StringValue(
+ QA_SITE_SETTINGS,
+ 'FEEDBACK_EMAILS',
+ description=_('Internal feedback form email recipients'),
+ help_text=_(
+ 'Comma separated list. If left empty, feedback mails are sent '
+ 'to admins and moderators'
+ ),
+ update_callback=feedback_emails_callback
+ )
+)
diff --git a/askbot/conf/skin_general_settings.py b/askbot/conf/skin_general_settings.py
index 11d201ad..beaf04be 100644
--- a/askbot/conf/skin_general_settings.py
+++ b/askbot/conf/skin_general_settings.py
@@ -6,6 +6,8 @@ from askbot.deps.livesettings import ConfigurationGroup
from askbot.deps.livesettings import values
from django.utils.translation import ugettext_lazy as _
from django.conf import settings as django_settings
+from django.core.validators import URLValidator
+from django.core.exceptions import ValidationError
from askbot.skins import utils as skin_utils
from askbot import const
from askbot.conf.super_groups import CONTENT_AND_UI
@@ -16,6 +18,32 @@ GENERAL_SKIN_SETTINGS = ConfigurationGroup(
super_group = CONTENT_AND_UI
)
+def logo_destination_callback(old_url, new_url):
+ url = new_url.strip()
+ if url == '':
+ return ''
+
+ if url.startswith('/'):
+ return url
+
+ validate = URLValidator()
+ try:
+ validate(url)
+ return url
+ except ValidationError:
+ raise ValueError(_('Please enter a valid url'))
+
+settings.register(
+ values.StringValue(
+ GENERAL_SKIN_SETTINGS,
+ 'LOGO_DESTINATION_URL',
+ default = '',
+ description = _('Custom destination URL for the logo'),
+ update_callback=logo_destination_callback
+ )
+)
+
+
settings.register(
values.ImageValue(
GENERAL_SKIN_SETTINGS,
diff --git a/askbot/conf/user_settings.py b/askbot/conf/user_settings.py
index d29e9278..c8e879b5 100644
--- a/askbot/conf/user_settings.py
+++ b/askbot/conf/user_settings.py
@@ -8,6 +8,7 @@ from django.conf import settings as django_settings
from askbot.skins import utils as skin_utils
from django.utils.translation import ugettext_lazy as _
from askbot import const
+import re
USER_SETTINGS = livesettings.ConfigurationGroup(
'USER_SETTINGS',
@@ -121,6 +122,23 @@ settings.register(
)
)
+def gravatar_url_callback(old, new):
+ """strips trailing slash"""
+ url_re = re.compile(r'([^/]*)/+$')
+ return url_re.sub(r'\1', new)
+
+settings.register(
+ livesettings.StringValue(
+ USER_SETTINGS,
+ 'GRAVATAR_BASE_URL',
+ description=_(
+ 'Base URL for the gravatar service'
+ ),
+ default='//www.gravatar.com/avatar',
+ update_callback=gravatar_url_callback
+ )
+)
+
settings.register(
livesettings.BooleanValue(
USER_SETTINGS,
@@ -133,7 +151,6 @@ settings.register(
)
)
-
settings.register(
livesettings.StringValue(
USER_SETTINGS,
diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py
index 35f9b26b..56da1c95 100644
--- a/askbot/const/__init__.py
+++ b/askbot/const/__init__.py
@@ -161,9 +161,12 @@ TAG_FIRST_CHARS = r'\w'
TAG_FORBIDDEN_FIRST_CHARS = r'#'
TAG_REGEX_BARE = r'%s[%s]+' % (TAG_FIRST_CHARS, TAG_CHARS)
TAG_REGEX = r'^%s$' % TAG_REGEX_BARE
-TAG_SPLIT_REGEX = r'[ ,]+'
+
+TAG_STRIP_CHARS = ', '
+TAG_SPLIT_REGEX = r'[%s]+' % TAG_STRIP_CHARS
TAG_SEP = ',' # has to be valid TAG_SPLIT_REGEX char and MUST NOT be in const.TAG_CHARS
#!!! see const.message_keys.TAG_WRONG_CHARS_MESSAGE
+
EMAIL_REGEX = re.compile(r'\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b', re.I)
TYPE_ACTIVITY_ASK_QUESTION = 1
diff --git a/askbot/deps/django_authopenid/backends.py b/askbot/deps/django_authopenid/backends.py
index f719e811..20533040 100644
--- a/askbot/deps/django_authopenid/backends.py
+++ b/askbot/deps/django_authopenid/backends.py
@@ -37,6 +37,7 @@ class AuthBackend(object):
provider_name = None,#required with all except email_key
openid_url = None,
email_key = None,
+ email = None, # used with mozilla-persona method
oauth_user_id = None,#used with oauth
facebook_user_id = None,#user with facebook
wordpress_url = None, # required for self hosted wordpress
@@ -129,7 +130,20 @@ class AuthBackend(object):
'duplicate openid url in the database!!! %s' % openid_url
)
return None
-
+
+ elif method == 'mozilla-persona':
+ try:
+ assoc = UserAssociation.objects.get(
+ openid_url=email,
+ provider_name='mozilla-persona'
+ )
+ return assoc.user
+ except UserAssociation.DoesNotExist:
+ return None
+ except UserAssociation.MultipleObjectsReturned:
+ logging.critical(
+ 'duplicate user with mozilla persona %s!!!' % email
+ )
elif method == 'email':
#with this method we do no use user association
@@ -144,6 +158,24 @@ class AuthBackend(object):
except User.DoesNotExist:
return None
+ elif method == 'valid_email':
+ try:
+ user = User.objects.get(email=email)
+ except User.DoesNotExist:
+ return None
+ except User.MultipleObjectsReturned:
+ LOG.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
+
+ if user.email_isvalid == False:
+ return None
+
+ return user
+
elif method == 'oauth':
if login_providers[provider_name]['type'] in ('oauth', 'oauth2'):
try:
diff --git a/askbot/deps/django_authopenid/forms.py b/askbot/deps/django_authopenid/forms.py
index 1f08b23c..9e069ed8 100644
--- a/askbot/deps/django_authopenid/forms.py
+++ b/askbot/deps/django_authopenid/forms.py
@@ -39,7 +39,7 @@ from django.conf import settings as django_settings
from askbot.conf import settings as askbot_settings
from askbot import const as askbot_const
from django.utils.safestring import mark_safe
-from recaptcha_works.fields import RecaptchaField
+from askbot.forms import AskbotRecaptchaField
from askbot.utils.forms import NextUrlField, UserNameField, UserEmailField, SetPasswordForm
from askbot.utils.loading import load_module
@@ -55,7 +55,6 @@ __all__ = [
'OpenidSigninForm','OpenidRegisterForm',
'ClassicRegisterForm', 'ChangePasswordForm',
'ChangeEmailForm', 'EmailPasswordForm', 'DeleteForm',
- 'ChangeOpenidForm'
]
class LoginProviderField(forms.CharField):
@@ -76,7 +75,7 @@ class LoginProviderField(forms.CharField):
if value in providers:
return value
else:
- error_message = 'unknown provider name %s' % cgi.escape(value)
+ error_message = u'unknown provider name %s' % value
logging.critical(error_message)
raise forms.ValidationError(error_message)
@@ -126,6 +125,10 @@ class LoginForm(forms.Form):
"""
next = NextUrlField()
login_provider_name = LoginProviderField()
+ persona_assertion = forms.CharField(
+ required=False,
+ widget=forms.widgets.HiddenInput()
+ )
openid_login_token = forms.CharField(
max_length=256,
required = False,
@@ -221,6 +224,8 @@ class LoginForm(forms.Form):
#self.do_clean_oauth_fields()
elif provider_type == 'wordpress_site':
self.cleaned_data['login_type'] = 'wordpress_site'
+ elif provider_type == 'mozilla-persona':
+ self.cleaned_data['login_type'] = 'mozilla-persona'
return self.cleaned_data
@@ -315,6 +320,14 @@ class OpenidRegisterForm(forms.Form):
username = UserNameField(widget_attrs={'tabindex': 0})
email = UserEmailField()
+class SafeOpenidRegisterForm(OpenidRegisterForm):
+ """this form uses recaptcha in addition
+ to the base register form
+ """
+ def __init__(self, *args, **kwargs):
+ super(SafeOpenidRegisterForm, self).__init__(*args, **kwargs)
+ self.fields['recaptcha'] = AskbotRecaptchaField()
+
class ClassicRegisterForm(SetPasswordForm):
""" legacy registration form """
@@ -328,10 +341,9 @@ class SafeClassicRegisterForm(ClassicRegisterForm):
"""this form uses recaptcha in addition
to the base register form
"""
- recaptcha = RecaptchaField(
- private_key = askbot_settings.RECAPTCHA_SECRET,
- public_key = askbot_settings.RECAPTCHA_KEY
- )
+ def __init__(self, *args, **kwargs):
+ super(SafeClassicRegisterForm, self).__init__(*args, **kwargs)
+ self.fields['recaptcha'] = AskbotRecaptchaField()
class ChangePasswordForm(forms.Form):
""" change password form """
@@ -480,5 +492,7 @@ def get_registration_form_class():
custom_class = getattr(django_settings, 'REGISTRATION_FORM', None)
if custom_class:
return load_module(custom_class)
+ elif askbot_settings.USE_RECAPTCHA:
+ return SafeOpenidRegisterForm
else:
return OpenidRegisterForm
diff --git a/askbot/deps/django_authopenid/urls.py b/askbot/deps/django_authopenid/urls.py
index 5103dce6..caab2037 100644
--- a/askbot/deps/django_authopenid/urls.py
+++ b/askbot/deps/django_authopenid/urls.py
@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*-
from django.conf import settings as django_settings
-from django.conf.urls.defaults import patterns, url
+try:
+ from django.conf.urls import patterns, url
+except ImportError:
+ from django.conf.urls.defaults import patterns, url
if django_settings.ASKBOT_TRANSLATE_URL == True:
from django.utils.translation import ugettext as _
diff --git a/askbot/deps/django_authopenid/util.py b/askbot/deps/django_authopenid/util.py
index 06379e1d..586136d6 100644
--- a/askbot/deps/django_authopenid/util.py
+++ b/askbot/deps/django_authopenid/util.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import cgi
+import httplib
import urllib
import urlparse
import functools
@@ -35,9 +36,9 @@ except:
import time, base64, hmac, hashlib, operator, logging
from models import Association, Nonce
-__all__ = ['OpenID', 'DjangoOpenIDStore', 'from_openid_response', 'clean_next']
+__all__ = ['OpenID', 'DjangoOpenIDStore', 'from_openid_response']
-ALLOWED_LOGIN_TYPES = ('password', 'oauth', 'openid-direct', 'openid-username', 'wordpress')
+ALLOWED_LOGIN_TYPES = ('password', 'oauth', 'oauth2', 'openid-direct', 'openid-username', 'wordpress')
class OpenID:
def __init__(self, openid_, issued, attrs=None, sreg_=None):
@@ -182,7 +183,8 @@ def filter_enabled_providers(data):
delete_list = list()
for provider_key, provider_settings in data.items():
name = provider_settings['name']
- is_enabled = getattr(askbot_settings, 'SIGNIN_' + name.upper() + '_ENABLED')
+ name_key = name.upper().replace('-', '_')
+ is_enabled = getattr(askbot_settings, 'SIGNIN_' + name_key + '_ENABLED')
if is_enabled == False:
delete_list.append(provider_key)
@@ -273,6 +275,15 @@ class LoginMethod(object):
self.oauth_authorize_url = self.get_required_attr('OAUTH_AUTHORIZE_URL', for_what)
self.oauth_get_user_id_function = self.get_required_attr('oauth_get_user_id_function', for_what)
+ if self.login_type == 'oauth2':
+ for_what = 'custom OAuth2 login'
+ self.auth_endpoint = self.get_required_attr('OAUTH_ENDPOINT', for_what)
+ self.token_endpoint = self.get_required_attr('OAUTH_TOKEN_ENDPOINT', for_what)
+ self.resource_endpoint = self.get_required_attr('OAUTH_RESOURCE_ENDPOINT', for_what)
+ self.oauth_get_user_id_function = self.get_required_attr('oauth_get_user_id_function', for_what)
+ self.response_parser = getattr(self.mod, 'response_parser', None)
+ self.token_transport = getattr(self.mod, 'token_transport', None)
+
if self.login_type.startswith('openid'):
self.openid_endpoint = self.get_required_attr('OPENID_ENDPOINT', 'custom OpenID login')
if self.login_type == 'openid-username':
@@ -294,7 +305,8 @@ class LoginMethod(object):
'change_password_prompt', 'consumer_key', 'consumer_secret',
'request_token_url', 'access_token_url', 'authorize_url',
'get_user_id_function', 'openid_endpoint', 'tooltip_text',
- 'check_password',
+ 'check_password', 'auth_endpoint', 'token_endpoint',
+ 'resource_endpoint', 'response_parser', 'token_transport'
)
#some parameters in the class have different names from those
#in the dictionary
@@ -388,6 +400,18 @@ def get_enabled_major_login_providers():
'password_changeable': True
}
+ if askbot_settings.SIGNIN_CUSTOM_OPENID_ENABLED:
+ context_dict = {'login_name': askbot_settings.SIGNIN_CUSTOM_OPENID_NAME}
+ data['custom_openid'] = {
+ 'name': 'custom_openid',
+ 'display_name': askbot_settings.SIGNIN_CUSTOM_OPENID_NAME,
+ 'type': askbot_settings.SIGNIN_CUSTOM_OPENID_MODE,
+ 'icon_media_path': askbot_settings.SIGNIN_CUSTOM_OPENID_LOGIN_BUTTON,
+ 'tooltip_text': _('Login with %(login_name)s') % context_dict,
+ 'openid_endpoint': askbot_settings.SIGNIN_CUSTOM_OPENID_ENDPOINT,
+ 'extra_token_name': _('%(login_name)s username') % context_dict
+ }
+
def get_facebook_user_id(client):
"""returns facebook user id given the access token"""
profile = client.request('me')
@@ -403,7 +427,8 @@ def get_enabled_major_login_providers():
'resource_endpoint': 'https://graph.facebook.com/',
'icon_media_path': '/jquery-openid/images/facebook.gif',
'get_user_id_function': get_facebook_user_id,
- 'response_parser': lambda data: dict(urlparse.parse_qsl(data))
+ 'response_parser': lambda data: dict(urlparse.parse_qsl(data)),
+ 'scope': ['email',],
}
if askbot_settings.TWITTER_KEY and askbot_settings.TWITTER_SECRET:
@@ -478,6 +503,12 @@ def get_enabled_major_login_providers():
'icon_media_path': '/jquery-openid/images/google.gif',
'openid_endpoint': 'https://www.google.com/accounts/o8/id',
}
+ data['mozilla-persona'] = {
+ 'name': 'mozilla-persona',
+ 'display_name': 'Mozilla Persona',
+ 'type': 'mozilla-persona',
+ 'icon_media_path': '/jquery-openid/images/mozilla-persona.gif',
+ }
data['yahoo'] = {
'name': 'yahoo',
'display_name': 'Yahoo',
@@ -825,7 +856,7 @@ def get_oauth2_starter_url(provider_name, csrf_token):
client_id=client_id,
redirect_uri=redirect_uri
)
- return client.auth_uri(state=csrf_token)
+ return client.auth_uri(state=csrf_token, scope=params['scope'])
def ldap_check_password(username, password):
@@ -838,3 +869,26 @@ def ldap_check_password(username, password):
except ldap.LDAPError, e:
logging.critical(unicode(e))
return False
+
+
+def mozilla_persona_get_email_from_assertion(assertion):
+ conn = httplib.HTTPSConnection('verifier.login.persona.org')
+ parsed_url = urlparse.urlparse(askbot_settings.APP_URL)
+ params = urllib.urlencode({
+ 'assertion': assertion,
+ 'audience': parsed_url.scheme + '://' + parsed_url.netloc
+ })
+ headers = {'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'}
+ conn.request('POST', '/verify', params, headers)
+ response = conn.getresponse()
+ if response.status == 200:
+ data = simplejson.loads(response.read())
+ email = data.get('email')
+ if email:
+ return email
+ else:
+ message = unicode(data)
+ message += '\nMost likely base url in /settings/QA_SITE_SETTINGS/ is incorrect'
+ raise ImproperlyConfigured(message)
+ #todo: nead more feedback to help debug fail cases
+ return None
diff --git a/askbot/deps/django_authopenid/views.py b/askbot/deps/django_authopenid/views.py
index 67df0bd0..e1a3b981 100644
--- a/askbot/deps/django_authopenid/views.py
+++ b/askbot/deps/django_authopenid/views.py
@@ -294,12 +294,12 @@ def complete_oauth2_signin(request):
client_id = getattr(
askbot_settings,
- provider_name.upper() + '_KEY'
+ provider_name.upper() + '_KEY',
)
client_secret = getattr(
askbot_settings,
- provider_name.upper() + '_SECRET'
+ provider_name.upper() + '_SECRET',
)
client = OAuth2Client(
@@ -307,12 +307,13 @@ def complete_oauth2_signin(request):
resource_endpoint=params['resource_endpoint'],
redirect_uri=site_url(reverse('user_complete_oauth2_signin')),
client_id=client_id,
- client_secret=client_secret
+ client_secret=client_secret,
+ token_transport=params.get('token_transport', None)
)
client.request_token(
code=request.GET['code'],
- parser=params['response_parser']
+ parser=params.get('response_parser', None)
)
#todo: possibly set additional parameters here
@@ -331,8 +332,8 @@ def complete_oauth2_signin(request):
if (provider_name == 'facebook'):
profile = client.request("me")
- request.session['email'] = profile['email']
- request.session['username'] = profile['username']
+ request.session['email'] = profile.get('email', '')
+ request.session['username'] = profile.get('username', '')
return finalize_generic_signin(
request = request,
@@ -536,6 +537,38 @@ def signin(request, template_name='authopenid/signin.html'):
)
raise Http404
+ elif login_form.cleaned_data['login_type'] == 'mozilla-persona':
+ assertion = login_form.cleaned_data['persona_assertion']
+ email = util.mozilla_persona_get_email_from_assertion(assertion)
+ if email:
+ user = authenticate(email=email, method='mozilla-persona')
+ if user is None:
+ user = authenticate(email=email, method='valid_email')
+ if user:
+ #create mozilla persona user association
+ #because we trust the given email address belongs
+ #to the same user
+ UserAssociation(
+ openid_url=email,
+ user=user,
+ provider_name='mozilla-persona',
+ last_used_timestamp=datetime.datetime.now()
+ ).save()
+
+ if user:
+ login(request, user)
+ return HttpResponseRedirect(next_url)
+
+ #else - create new user account
+ #pre-fill email address with persona registration
+ request.session['email'] = email
+ return finalize_generic_signin(
+ request,
+ login_provider_name = 'mozilla-persona',
+ user_identifier = email,
+ redirect_url = next_url
+ )
+
elif login_form.cleaned_data['login_type'] == 'openid':
#initiate communication process
logging.debug('processing signin with openid submission')
@@ -805,7 +838,7 @@ def change_password(request):
data['message'] = _('Your new password is saved')
else:
data['errors'] = form.errors
- return HttpResponse(simplejson.dumps(data), mimetype='application/json')
+ return HttpResponse(simplejson.dumps(data), content_type='application/json')
@login_required
def delete_login_method(request):
@@ -963,6 +996,7 @@ def finalize_generic_signin(
@not_authenticated
@csrf.csrf_protect
+@fix_recaptcha_remote_ip
def register(request, login_provider_name=None, user_identifier=None):
"""
this function is used via it's own url with request.method=POST
diff --git a/askbot/deps/group_messaging/urls.py b/askbot/deps/group_messaging/urls.py
index 19ee35bb..79536770 100644
--- a/askbot/deps/group_messaging/urls.py
+++ b/askbot/deps/group_messaging/urls.py
@@ -1,6 +1,9 @@
"""url configuration for the group_messaging application"""
-from django.conf.urls.defaults import patterns
-from django.conf.urls.defaults import url
+try:
+ from django.conf.urls import patterns, url
+except ImportError:
+ from django.conf.urls.defaults import patterns, url
+
from group_messaging import views
urlpatterns = patterns('',
diff --git a/askbot/deps/group_messaging/views.py b/askbot/deps/group_messaging/views.py
index 244762d1..f2ddaef3 100644
--- a/askbot/deps/group_messaging/views.py
+++ b/askbot/deps/group_messaging/views.py
@@ -45,7 +45,7 @@ class InboxView(object):
template = get_template(template_name)
html = template.render(context)
json = simplejson.dumps({'html': html, 'success': True})
- return HttpResponse(json, mimetype='application/json')
+ return HttpResponse(json, content_type='application/json')
def get(self, request, *args, **kwargs):
@@ -120,7 +120,7 @@ class NewThread(InboxView):
)
result['success'] = True
result['message_id'] = message.id
- return HttpResponse(simplejson.dumps(result), mimetype='application/json')
+ return HttpResponse(simplejson.dumps(result), content_type='application/json')
class PostReply(InboxView):
diff --git a/askbot/deps/livesettings/urls.py b/askbot/deps/livesettings/urls.py
index b608bf1f..f628f830 100644
--- a/askbot/deps/livesettings/urls.py
+++ b/askbot/deps/livesettings/urls.py
@@ -1,4 +1,7 @@
-from django.conf.urls.defaults import *
+try:
+ from django.conf.urls import *
+except ImportError:
+ from django.conf.urls.defaults import *
urlpatterns = patterns('askbot.deps.livesettings.views',
url(r'^$', 'site_settings', {}, name='site_settings'),
diff --git a/askbot/doc/source/askbot/layout.html b/askbot/doc/source/askbot/layout.html
index f1c8b509..6fd6b0d1 100644
--- a/askbot/doc/source/askbot/layout.html
+++ b/askbot/doc/source/askbot/layout.html
@@ -10,10 +10,10 @@
<div class="ab-proj-header">
<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="https://askbot.com/hire-us/" alt='Hire Us'>Consulting Services</a> |
<a href="/doc/index.html" alt="Documentation">Documentation</a> |
- <a href="/contribute" alt='Contribute'>Contribute</a> |
- <a href="/feedback/" alt='contact'>Contact</a>
+ <a href="https://askbot.com/contribute" alt='Contribute'>Contribute</a> |
+ <a href="https://askbot.com/feedback/" alt='contact'>Contact</a>
</div>
{% endblock %}
{% block relbar2 %}
diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst
index 73aeee7e..67928fbc 100644
--- a/askbot/doc/source/changelog.rst
+++ b/askbot/doc/source/changelog.rst
@@ -3,6 +3,12 @@ Changes in Askbot
Development master branch (only on github)
------------------------------------------
+* Allow custom destination url under the logo
+* Option to allow asking without registration (Egil Moeller)
+* Implemented Mozilla Persona authentication
+* Allowed custom providers of gravatar service (michas2)
+* Allowed configurable custom OpenID login button
+* Allowed custom list of feedback recipients (Keto)
* Added option to show user's emails to the moderators
* Added Read-Only mode for the site in the "access control" section.
* Added `askbot_add_osqa_content` management command.
diff --git a/askbot/doc/source/contributors.rst b/askbot/doc/source/contributors.rst
index 0dfb942b..d1e2dd3a 100644
--- a/askbot/doc/source/contributors.rst
+++ b/askbot/doc/source/contributors.rst
@@ -54,6 +54,11 @@ Programming, bug fixes and documentation
* `Benjamin Abel <https://github.com/BenjaminABEL>`_
* `Pami Ketolainen <https://github.com/keto>`_
* `Hamdi <https://github.com/Hamdy>`_
+* `michas2 <https://github.com/michas2>`_
+* `Francis Devereux <https://github.com/frankoid>`_
+* `Andrew Chen <https://github.com/yongjhih>`_
+* `Egil Moeller <https://github.com/redhog>`_
+* `Jerry Zhenlei Cai <https://github.com/jerryzhenleicai>`_
Translations
------------
diff --git a/askbot/doc/source/index.rst b/askbot/doc/source/index.rst
index 64ab1b20..d6703775 100644
--- a/askbot/doc/source/index.rst
+++ b/askbot/doc/source/index.rst
@@ -28,8 +28,9 @@ at the forum_ or by email at admin@askbot.org
Appendix E: Askbot as reusable Django application <askbot-as-reusable-django-application>
Appendix F: Customizing skin in askbot <customizing-skin-in-askbot>
Appendix G: Intranet setup <intranet-setup>
- Appendix H: Haystack with Solr and Apache Tomcat <solr>
- Appendix I: Migration from MySQL to PostgreSQL <mysql-to-postgres>
+ Appendix H: Language support in Askbot <localization>
+ Appendix I: Configuration of text search <text-search>
+ Appendix J: Migration from MySQL to PostgreSQL <mysql-to-postgres>
Footnotes <footnotes>
Contributors <contributors>
Changelog <changelog>
diff --git a/askbot/doc/source/localization.rst b/askbot/doc/source/localization.rst
new file mode 100644
index 00000000..fbf51da6
--- /dev/null
+++ b/askbot/doc/source/localization.rst
@@ -0,0 +1,81 @@
+.. _localization:
+======================================
+Configuring language support in Askbot
+======================================
+
+There are several things to consider when localizing askbot:
+
+* :ref:`setting the site language <default-lang>`
+* :ref:`translation and display of the urls <translate-urls>`
+* :ref:`translation of the strings in the user interface <strings>`
+* :ref:`enabling the multilingual setup <multilingual>`
+* :ref:`configuring the language-specific text search <text-search>`
+
+.. _default-lang:
+
+Setting the site language
+=========================
+
+Specify the language code with the value of `LANGUAGE_CODE` parameter
+in the `settings.py` file::
+
+ LANGUAGE_CODE='es'
+
+.. note::
+ In the :ref:`multi-lingual configuration <multilingual>`
+ this language will be the default and the complete list of
+ language codes and their verbose names
+ is specified with the `LANGUAGES` parameter.
+
+.. _translate-urls:
+
+Translation of the URLs
+=======================
+
+There are also `settings.py` options to translate the urls:
+`ASKBOT_TRANSLATE_URL` and `ALLOW_UNICODE_SLUGS`.
+
+When the `ASKBOT_TRANSLATE_URL` is `True`, most urls will be translated,
+otherwise urls will be in English.
+When the `ALLOW_UNICODE_SLUGS` is `True` the question titles and user names
+will be presented as Unicode, e.g. with the Cyrillic, Chinese
+or Arabic characters, otherwise they will be transliterated into ASCII.
+
+If you are translating URLs (in the transifex you will probably
+find them as strings containing forward slashes) -
+take the following, in order to prevent broken links:
+
+* translation of multiple urls cannot be the same
+ (e.g. /question/ and /questions/ must have different translations)
+* if the same url is present in more than one translation file
+ those translation must be exactly the same
+
+.. _strings:
+
+Translation of strings in Askbot
+================================
+
+Translation of Askbot strings is performed at the `Transifex service <transifex>`_.
+Please `register there <transifex>`_ and work on the localization that interests you.
+We periodically update the source language strings on Transifex and pull
+the translations back into the project. Thanks!
+
+If you intend to translate urls - please :ref:`look here <translate-urls>`.
+
+Please *do not* translate via github (if you know what it means),
+as it's better to have just one source of strings.
+
+The remaining part will will most likely interest developers,
+therefore here we tell what is specific to Askbot and
+refer the developer to the documentation of tools
+used in Askbot.
+
+Firstly - Askbot uses `Jinja2 <http://jinja.pocoo.org/docs/>`_ templates,
+not the Django templates and an Jinja2 adapter module for Django, called
+`Coffin <https://github.com/coffin/coffin/>`_. Please look at how translation
+tags are added to the templates processed by the `coffin` module.
+
+Secondly - instead of the django `makemessages` command - use `jinja2_makemessages`.
+
+Finally - to pull strings from the transifex use the `tx` program from
+`transifex-client pypi package <https://pypi.python.org/pypi/transifex-client>`_.
diff --git a/askbot/doc/source/multilingual.rst b/askbot/doc/source/multilingual.rst
new file mode 100644
index 00000000..75f93f0a
--- /dev/null
+++ b/askbot/doc/source/multilingual.rst
@@ -0,0 +1,46 @@
+.. _multilingual:
+====================================
+Setting up multilingual Askbot sites
+====================================
+
+Askbot can support multiple languages on a single site, in which case
+urls are modified by a prefix made of a language code, e.g.
+base url /questions/ becomes /de/questions/ for the German localization.
+
+.. note::
+ If you want to learn about configuration of individual languages
+ please look :ref:`here <localization>`
+
+In order to enable the multilingual setup add the following to the
+`settings.py` file::
+
+ ASKBOT_MULTILINGUAL=True
+
+Also, activate the django's locale middleware by adding to the
+`MIDDLEWARE_CLASSES` the following entry::
+
+ 'django.middleware.locale.LocaleMiddleware',
+
+There is a standard Django setting `LANGUAGES`, which enables specific languages.
+By default this setting contains very many languages.
+You will likely want to narrow in the `settings.py` file
+the choice of the available languages::
+
+ #it's important to use ugettext_lazy or ugettext_noop
+ #in the settings.py file
+ from django.utils.translation import ugettext_lazy as _
+ LANGUAGES = (
+ ('de', _('German')),
+ ('en', _('English'))
+ )
+
+More on the usage of this setting can be read in the
+`Django documentation <https://docs.djangoproject.com/en/dev/ref/settings/#languages>`_.
+
+The default language should be specified with the setting `LANGUAGE_CODE`.
+Users will be automatically redirected to the corresponding default language
+page from the non-prefixed urls.
+
+There are a number of `settings.py` options that control the various
+aspects of the site localization - the behaviour of the software depending on the
+currently active language.. Please read more about the :ref:`Localization of Askbot <localization>`.
diff --git a/askbot/doc/source/solr.rst b/askbot/doc/source/solr.rst
index 9db6ba2f..8a1de0c2 100644
--- a/askbot/doc/source/solr.rst
+++ b/askbot/doc/source/solr.rst
@@ -8,22 +8,26 @@ Installing Apache Solr with Apache Tomcat 7 in Ubuntu 12.04
This document describes the process of instalation of Apache Solr search engine in Ubuntu Server 12.04
for askbot use. To follow this steps you must have already askbot installed and running.
-Getting the requirements
-========================
+Installation of the required packages
+=====================================
-We need the following packages installed::
+Install packages `tomcat7` and `tomcat7-admin`::
sudo apt-get install tomcat7 tomcat7-admin
-We need to download Apache Solr from the `official site <http://lucene.apache.org/solr/downloads.html>`_::
+Download Apache Solr from the `official site <http://lucene.apache.org/solr/downloads.html>`_::
wget http://www.bizdirusa.com/mirrors/apache/lucene/solr/3.6.2/apache-solr-3.6.2.tgz
+Install `django-haystack` module in your Python environment::
+
+ pip install django-haystack
+
Setting up Tomcat
=================
-After installing tomcat there are some configuration required to make it work. First we are going to add
-Tomcat users. Edit /etc/tomcat7/tomcat-users.xml and add the following::
+After installing Tomcat, add users to the Tomcat server.
+Edit `/etc/tomcat7/tomcat-users.xml` and add the following::
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
@@ -31,15 +35,17 @@ Tomcat users. Edit /etc/tomcat7/tomcat-users.xml and add the following::
<role rolename="admin"/>
<role rolename="admin-gui"/>
<role rolename="manager-gui"/>
- <user username="tomcat" password="tomcat" roles="manager,admin,manager-gui,admin-gui"/>
+ <user username="tomcat" password="tomcat"
+ roles="manager,admin,manager-gui,admin-gui"/>
</tomcat-users>
-This will allow you to connect to the web management interface. After doing it restart the service:
+Then restart the service::
service tomcat7 restart
-To make see if it works go to: http://youripaddress:8080/manager it will ask for your tomcat user password
-described in the tomcat-users.xml
+Now you should be able to connect to the web management interface
+via http://youripaddress:8080/manager
+and entering there user name and password.
Installing Solr under Tomcat
============================
@@ -48,28 +54,30 @@ Extract the solr tar archive from the previous download::
tar -xzf apache-solr-3.6.2.tgz
-Copy the example/ directory from the source to /opt/solr/. Open the file /opt/solr/example/solr/conf/solrconfig.xml
+Copy the `example/` directory from the source to `/opt/solr/`.
+Open the file `/opt/solr/example/solr/conf/solrconfig.xml`
and Modify the dataDir parameter as::
<dataDir>${solr.data.dir:/opt/solr/example/solr/data}</dataDir>
-Copy the .war file in dist directory to /opt/solr::
+Copy the `.war` file in dist directory to `/opt/solr`::
cp dist/apache-solr-3.6.2.war /opt/solr
-Create solr.xml inside of /etc/tomcat/Catalina/localhost/ with the following contents::
+Create `solr.xml` inside of `/etc/tomcat/Catalina/localhost/` with the following contents::
<?xml version="1.0" encoding="utf-8"?>
<Context docBase="/opt/solr/apache-solr-3.6.2.war" debug="0" crossContext="true">
- <Environment name="solr/home" type="java.lang.String" value="/opt/solr/example/solr" override="true"/>
+ <Environment name="solr/home" type="java.lang.String"
+ value="/opt/solr/example/solr" override="true"/>
</Context>
-Restart tomcat server::
+Restart the tomcat server::
service tomcat7 restart
-By now you should be able to see the "solr" application in the tomcat manager and also access it in /solr/admin.
-
+Now you should be able to access the "solr" application
+in the Tomcat manager at `/solr/admin`.
Configuring Askbot with Solr
============================
@@ -103,14 +111,18 @@ The output should be something like::
Indexing 101 posts.
Indexing 101 threads.
-You must be good to go after this, just restart the askbot application and test the search with haystack and solr
+Now all should be ready,
+just restart the askbot application
+and test the search with haystack and solr.
+.. _solr-multilingual:
Multilingual Setup
==================
.. note::
- This is experimental feature, currently xml generation works for: English, Spanish, Chinese, Japanese, Korean and French.
+ This is experimental feature, currently xml generation works for:
+ English, Spanish, Chinese, Japanese, Korean and French.
Add the following to settings.py::
@@ -129,7 +141,6 @@ Configure the HAYSTACK_CONNECTIONS settings with the following format for each l
},
}
-
Generate xml files according to language::
python manage.py askbot_build_solr_schema -l <language_code> > /opt/solr/example/solr/conf/schema-<language_code>.xml
@@ -141,12 +152,13 @@ For each language that you want to support you will need to add a solr core like
http://127.0.0.1:8080/solr/admin/cores?action=CREATE&name=core-<language_code>&instanceDir=.&config=solrconfig.xml&schema=schema-<language_code>.xml&dataDir=data
-For more information on how to handle Solr cores visit `the oficial Solr documetation wiki. <http://wiki.apache.org/solr/CoreAdmin>`_
+For more information on how to handle Solr cores visit the
+`Solr documetation <http://wiki.apache.org/solr/CoreAdmin>`_.
Build the index according to language
-------------------------------------
-For every language supported you'll need to rebuild the index the following way::
+For every active language rebuild the index::
python manage.py askbot_rebuild_index -l <language_code>
@@ -159,24 +171,32 @@ There are several ways to keep the index fresh in askbot with haystack.
Cronjob
-------
-Create a cronjob that executes *askbot_update_index* command for each language installed (in case of multilingual setup).
+Create a cronjob that executes *askbot_update_index* command
+for each of the activated languages.
Real Time Signal
----------------
-The real time signal method updates the index synchronously after each object it's saved or deleted, to enable it add this to settings.py::
+The *real time* signal method updates the index synchronously
+after each object it's saved or deleted,
+to enable it add this to settings.py::
HAYSTACK_SIGNAL_PROCESSOR = 'askbot.search.haystack.signals.AskbotRealtimeSignalProcessor'
-this can delay the requests time of your page, if you have a high traffic site this is not recommended.
+Use of synchronous index updates may slow down your site
+which may not be acceptable for the high traffic sites.
+
+Updating the Index asyncronously with Celery
+--------------------------------------------
-Updating the Index with Celery
-------------------------------
+The *asynchronous signal* method updates the index by adding delayed job to the queue
+after each object is saved or deleted.
-The real time signal method updates the index asynchronously after each object it's saved or deleted using Celery as queue to enable it add this to settings.py::
+To make this work,
+`django-celery <http://celery.readthedocs.org/en/latest/django/first-steps-with-django.html>`_
+must be installed, enabled and configured and the Haystack signal processor configured
+in the `settings.py` file::
HAYSTACK_SIGNAL_PROCESSOR = 'askbot.search.haystack.signals.AskbotCelerySignalProcessor'
#modify CELERY_ALWAYS_EAGER to:
CELERY_ALWAYS_EAGER = False
-
-You will need to enable Celery to make this work.
diff --git a/askbot/doc/source/text-search.rst b/askbot/doc/source/text-search.rst
new file mode 100644
index 00000000..b18e3f33
--- /dev/null
+++ b/askbot/doc/source/text-search.rst
@@ -0,0 +1,48 @@
+.. _text-search:
+======================================
+Configuring full text search in Askbot
+======================================
+
+Currently there are two supported language-aware mechanisms for full text search:
+
+* :ref:`postgresql full text search <postgresql-text-search>`
+* :ref:`Solr search engine <solr-text-search>`
+
+MySQL supports text search only for English and only for the MyISAM storage engine.
+MyISAM engine lacks support of the database transactions,
+therefore it is strongly recommended to use Postgresql.
+
+.. _postgresql-text-search:
+
+Postgresql full text search
+===========================
+
+Postgresql supports full text search in the following languages:
+
+Danish, Dutch, English, Finnish, French, German, Hungarian,
+Italian, Japanese (requires postgresql package `textsearch_ja`), Norwegian,
+Portugese, Romanian, Russian, Spanish, Swedish, Turkish.
+
+To enable this option - just use the postgresql database and
+add in the `settings.py` file
+the corresponding entry in the
+`LANGUAGES setting <https://docs.djangoproject.com/en/dev/ref/settings/#languages>`_.
+
+.. note::
+ Japanese language search in Postgresql requires installation
+ of a "contrib" package called `textsearch_ja`
+
+.. _solr-text-search:
+
+Solr full text search
+=====================
+
+Apache Solr search supports more languages and Askbot supports Solr via the
+module called Haystack.
+
+:ref:`Here <solr>` are detailed instructions on how to enable Solr on
+Ubuntu system version 12.04, which may be helpful for users of other
+distributions of Linux.
+
+In addition to the basic set up of Solr, it will be necessary to configure
+:ref:`multilingual search <solr-multilingual>` under solr.
diff --git a/askbot/feed.py b/askbot/feed.py
index c0736739..b8f7efb7 100644
--- a/askbot/feed.py
+++ b/askbot/feed.py
@@ -88,9 +88,9 @@ class RssIndividualQuestionFeed(Feed):
if item.post_type == "question":
title = item.thread.title
elif item.post_type == "answer":
- title = "Answer by %s for %s " % (item.author, item.thread._question_post().summary)
+ title = u'Answer by %s for %s ' % (item.author, item.thread._question_post().summary)
elif item.post_type == "comment":
- title = "Comment by %s for %s" % (item.author, item.parent.summary)
+ title = u'Comment by %s for %s' % (item.author, item.parent.summary)
return title
def item_description(self, item):
diff --git a/askbot/forms.py b/askbot/forms.py
index a490a4ac..e3c6c253 100644
--- a/askbot/forms.py
+++ b/askbot/forms.py
@@ -12,6 +12,7 @@ from django.utils.html import strip_tags
from django.utils.datastructures import SortedDict
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy, string_concat
+from django.utils.translation import get_language
from django.utils.text import get_text_list
from django.contrib.auth.models import User
from django_countries import countries
@@ -23,6 +24,10 @@ from askbot.conf import get_tag_display_filter_strategy_choices
from tinymce.widgets import TinyMCE
import logging
+def should_use_recaptcha(user):
+ """True if user must use recaptcha"""
+ return askbot_settings.USE_RECAPTCHA and (user.is_anonymous() or user.is_watched())
+
def cleanup_dict(dictionary, key, empty_value):
"""deletes key from dictionary if it exists
@@ -69,12 +74,14 @@ def clean_marked_tagnames(tagnames):
if tagname == '':
continue
if tagname.endswith('*'):
- if tagname.count('*') > 1:
+ if tagname.count('*') > 1 or len(tagname) == 1:
continue
else:
- wildcards.append(tagname)
+ base_tag = tagname[:-1]
+ cleaned_base_tag = clean_tag(base_tag, look_in_db=False)
+ wildcards.append(cleaned_base_tag + '*')
else:
- pure_tags.append(tagname)
+ pure_tags.append(clean_tag(tagname))
return pure_tags, wildcards
@@ -207,6 +214,14 @@ class CountedWordsField(forms.CharField):
return value
+class AskbotRecaptchaField(RecaptchaField):
+ """A recaptcha field with preset keys from the livesettings"""
+ def __init__(self, *args, **kwargs):
+ kwargs.setdefault('private_key', askbot_settings.RECAPTCHA_SECRET)
+ kwargs.setdefault('public_key', askbot_settings.RECAPTCHA_KEY)
+ super(AskbotRecaptchaField, self).__init__(*args, **kwargs)
+
+
class LanguageField(forms.ChoiceField):
def __init__(self, *args, **kwargs):
@@ -354,7 +369,7 @@ class AnswerEditorField(EditorField):
self.min_length = askbot_settings.MIN_ANSWER_BODY_LENGTH
-def clean_tag(tag_name):
+def clean_tag(tag_name, look_in_db=True):
"""a function that cleans a single tag name"""
tag_length = len(tag_name)
if tag_length > askbot_settings.MAX_TAG_LENGTH:
@@ -383,9 +398,14 @@ def clean_tag(tag_name):
if askbot_settings.FORCE_LOWERCASE_TAGS:
#a simpler way to handle tags - just lowercase thew all
return tag_name.lower()
+ elif look_in_db == False:
+ return tag_name
else:
from askbot import models
- matching_tags = models.Tag.objects.filter(name__iexact=tag_name)
+ matching_tags = models.Tag.objects.filter(
+ name__iexact=tag_name,
+ language_code=get_language()
+ )
if len(matching_tags) > 0:
return matching_tags[0].name
else:
@@ -420,7 +440,7 @@ class TagNamesField(forms.CharField):
def clean(self, value):
from askbot import models
value = super(TagNamesField, self).clean(value)
- data = value.strip()
+ data = value.strip(const.TAG_STRIP_CHARS)
if len(data) < 1:
if askbot_settings.TAGS_ARE_REQUIRED:
raise forms.ValidationError(
@@ -713,19 +733,11 @@ class SendMessageForm(forms.Form):
)
-class NotARobotForm(forms.Form):
- recaptcha = RecaptchaField(
- private_key=askbot_settings.RECAPTCHA_SECRET,
- public_key=askbot_settings.RECAPTCHA_KEY
- )
-
-
class FeedbackForm(forms.Form):
name = forms.CharField(label=_('Your name (optional):'), required=False)
email = forms.EmailField(label=_('Email:'), required=False)
message = forms.CharField(
label=_('Your message:'),
- max_length=800,
widget=forms.Textarea(attrs={'cols': 60})
)
no_email = forms.BooleanField(
@@ -734,22 +746,21 @@ class FeedbackForm(forms.Form):
)
next = NextUrlField()
- def __init__(self, is_auth=False, *args, **kwargs):
+ def __init__(self, user=None, *args, **kwargs):
super(FeedbackForm, self).__init__(*args, **kwargs)
- self.is_auth = is_auth
- if not is_auth:
- if askbot_settings.USE_RECAPTCHA:
- self._add_recaptcha_field()
-
- def _add_recaptcha_field(self):
- self.fields['recaptcha'] = RecaptchaField(
- private_key=askbot_settings.RECAPTCHA_SECRET,
- public_key=askbot_settings.RECAPTCHA_KEY
- )
+ self.user = user
+ if should_use_recaptcha(user):
+ self.fields['recaptcha'] = AskbotRecaptchaField()
+
+ def clean_message(self):
+ message = self.cleaned_data.get('message', '').strip()
+ if not message:
+ raise forms.ValidationError(_('Message is required'))
+ return message
def clean(self):
super(FeedbackForm, self).clean()
- if not self.is_auth:
+ if self.user and self.user.is_anonymous():
if not self.cleaned_data['no_email'] \
and not self.cleaned_data['email']:
msg = _('Please mark "I dont want to give my mail" field.')
@@ -920,15 +931,17 @@ class AskForm(PostAsSomeoneForm, PostPrivatelyForm):
self.fields['ask_anonymously'] = forms.BooleanField(
label=_('post anonymously'),
- required=False,
+ required=False
)
- #hide ask_anonymously field
+ if user.is_anonymous() or not askbot_settings.ALLOW_ASK_ANONYMOUSLY:
+ self.hide_field('ask_anonymously')
+
if getattr(django_settings, 'ASKBOT_MULTILINGUAL', False):
self.fields['language'] = LanguageField()
- if askbot_settings.ALLOW_ASK_ANONYMOUSLY is False:
- self.hide_field('ask_anonymously')
+ if should_use_recaptcha(user):
+ self.fields['recaptcha'] = AskbotRecaptchaField()
def clean_ask_anonymously(self):
"""returns false if anonymous asking is not allowed
@@ -937,7 +950,6 @@ class AskForm(PostAsSomeoneForm, PostPrivatelyForm):
self.cleaned_data['ask_anonymously'] = False
return self.cleaned_data['ask_anonymously']
-
ASK_BY_EMAIL_SUBJECT_HELP = _(
'Subject line is expected in the format: '
'[tag1, tag2, tag3,...] question title'
@@ -957,7 +969,7 @@ class AskWidgetForm(forms.Form, FormWithHideableFields):
super(AskWidgetForm, self).__init__(*args, **kwargs)
self.fields['title'] = TitleField()
#hide ask_anonymously field
- if not askbot_settings.ALLOW_ASK_ANONYMOUSLY:
+ if user.is_anonymous() or not askbot_settings.ALLOW_ASK_ANONYMOUSLY:
self.hide_field('ask_anonymously')
self.fields['text'] = QuestionEditorField(user=user)
if not include_text:
@@ -966,6 +978,9 @@ class AskWidgetForm(forms.Form, FormWithHideableFields):
self.fields['text'].required = False
self.fields['text'].min_length = 0
+ if should_use_recaptcha(user):
+ self.fields['recaptcha'] = AskbotRecaptchaField()
+
class CreateAskWidgetForm(forms.Form, FormWithHideableFields):
title = forms.CharField(max_length=100)
include_text_field = forms.BooleanField(required=False)
@@ -1109,7 +1124,11 @@ class AnswerForm(PostAsSomeoneForm, PostPrivatelyForm):
def __init__(self, *args, **kwargs):
super(AnswerForm, self).__init__(*args, **kwargs)
- self.fields['text'] = AnswerEditorField(user=kwargs['user'])
+ user = kwargs['user']
+ self.fields['text'] = AnswerEditorField(user=user)
+
+ if should_use_recaptcha(user):
+ self.fields['recaptcha'] = AskbotRecaptchaField()
def has_data(self):
"""True if form is bound or has inital data"""
@@ -1225,6 +1244,9 @@ class EditQuestionForm(PostAsSomeoneForm, PostPrivatelyForm):
if getattr(django_settings, 'ASKBOT_MULTILINGUAL', False):
self.fields['language'] = LanguageField()
+ if should_use_recaptcha(self.user):
+ self.fields['recaptcha'] = AskbotRecaptchaField()
+
def has_changed(self):
if super(EditQuestionForm, self).has_changed():
return True
@@ -1332,6 +1354,9 @@ class EditAnswerForm(PostAsSomeoneForm, PostPrivatelyForm):
self.fields['text'].initial = revision.text
self.fields['wiki'].initial = answer.wiki
+ if should_use_recaptcha(user):
+ self.fields['recaptcha'] = AskbotRecaptchaField()
+
def has_changed(self):
#todo: this function is almost copy/paste of EditQuestionForm.has_changed()
if super(EditAnswerForm, self).has_changed():
@@ -1698,5 +1723,5 @@ class EditCommentForm(forms.Form):
suppress_email = SuppressEmailField()
-class DeleteCommentForm(forms.Form):
+class ProcessCommentForm(forms.Form):
comment_id = forms.IntegerField()
diff --git a/askbot/importers/stackexchange/management/commands/load_stackexchange.py b/askbot/importers/stackexchange/management/commands/load_stackexchange.py
index ff6ddb15..902bc988 100644
--- a/askbot/importers/stackexchange/management/commands/load_stackexchange.py
+++ b/askbot/importers/stackexchange/management/commands/load_stackexchange.py
@@ -915,7 +915,7 @@ it may be helpful to split this procedure in two:\n
u = askbot.User()
u_type = se_u.user_type.name
if u_type == 'Administrator':
- u.set_admin_status()
+ u.set_status('d')
elif u_type == 'Moderator':
u.set_status('m')
elif u_type not in ('Unregistered', 'Registered'):
diff --git a/askbot/locale/fr/LC_MESSAGES/django.po b/askbot/locale/fr/LC_MESSAGES/django.po
index 76ec2105..7f77242a 100644
--- a/askbot/locale/fr/LC_MESSAGES/django.po
+++ b/askbot/locale/fr/LC_MESSAGES/django.po
@@ -604,7 +604,7 @@ msgstr "Veuillez utiliser des espaces pour séparer les entrées, ne pas utilise
#: conf/badges.py:13
msgid "Badge settings"
-msgstr "Réglages des médailles"
+msgstr "Réglages des badges"
#: conf/badges.py:23
msgid "Disciplined: minimum upvotes for deleted post"
@@ -1343,7 +1343,7 @@ msgstr ""
#: conf/karma_and_badges_visibility.py:12
msgid "Karma & Badge visibility"
-msgstr "Visibilité de l'aura et des médailles "
+msgstr "Visibilité de l'aura et des badges "
#: conf/karma_and_badges_visibility.py:27
msgid "Visibility of karma"
@@ -1355,11 +1355,11 @@ msgstr "L'aura d'un utilisateur peut être montrée, soit publiquement, soit uni
#: conf/karma_and_badges_visibility.py:44
msgid "Visibility of badges"
-msgstr "Visibilité des médailles"
+msgstr "Visibilité des badges"
#: conf/karma_and_badges_visibility.py:47
msgid "Badges can be either publicly shown or completely hidden"
-msgstr "Les médailles peuvent être soit montrées publiquement, soit complètement cachées"
+msgstr "Les badges peuvent être soit montrées publiquement, soit complètement cachées"
#: conf/ldap.py:9
msgid "LDAP login configuration"
@@ -2052,7 +2052,7 @@ msgid ""
"values, more suitable for the larger communities, <strong>WARNING:</strong> "
"your current values for Minimum reputation, Badge Settings and Vote Rules "
"will be changed after you modify this setting."
-msgstr "Le mode \"grand site\" augmente les seuils de réputation et de certaines médailles à des valeurs mieux adaptées à des communautés plus grandes. <strong>AVERTISSEMENT :</strong> les valeurs actuelles de la réputation minimum, des réglages des médailles et des règles de vote seront changées après que vous ayez modifié ce réglage."
+msgstr "Le mode \"grand site\" augmente les seuils de réputation et de certaines badges à des valeurs mieux adaptées à des communautés plus grandes. <strong>AVERTISSEMENT :</strong> les valeurs actuelles de la réputation minimum, des réglages des badges et des règles de vote seront changées après que vous ayez modifié ce réglage."
#: conf/site_settings.py:14
msgid "URLS, keywords & greetings"
@@ -2429,7 +2429,7 @@ msgstr "Clé Akismet pour activer la détection du spam"
#: conf/super_groups.py:5
msgid "Reputation, Badges, Votes & Flags"
-msgstr "Réputation, médailles, votes et marqueurs"
+msgstr "Réputation, badges, votes et marqueurs"
#: conf/super_groups.py:6
msgid "Static Content, URLS & UI"
@@ -2728,7 +2728,7 @@ msgstr "réponse modifiée"
#: const/__init__.py:201
msgid "received badge"
-msgstr "médaille reçue"
+msgstr "badge reçue"
#: const/__init__.py:202
msgid "marked best answer"
@@ -3853,22 +3853,22 @@ msgstr "%(username)s a une aura de %(reputation)s"
#, python-format
msgid "one gold badge"
msgid_plural "%(count)d gold badges"
-msgstr[0] "une médaille d'or"
-msgstr[1] "%(count)d médailles d'or"
+msgstr[0] "un badge d'or"
+msgstr[1] "%(count)d badges d'or"
#: models/__init__.py:2515
#, python-format
msgid "one silver badge"
msgid_plural "%(count)d silver badges"
-msgstr[0] "une médaille d'argent"
-msgstr[1] "%(count)d médailles d'argent"
+msgstr[0] "un badge d'argent"
+msgstr[1] "%(count)d badges d'argent"
#: models/__init__.py:2522
#, python-format
msgid "one bronze badge"
msgid_plural "%(count)d bronze badges"
-msgstr[0] "une médaille de bronze"
-msgstr[1] "%(count)d médailles de bronze"
+msgstr[0] "un badge de bronze"
+msgstr[1] "%(count)d badges de bronze"
#: models/__init__.py:2533
#, python-format
@@ -3931,7 +3931,7 @@ msgstr "\"%(title)s\""
msgid ""
"Congratulations, you have received a badge '%(badge_name)s'. Check out <a "
"href=\"%(user_profile)s\">your profile</a>."
-msgstr "Félicitations, vous avez reçu une médaille '%(badge_name)s'. Consultez <a href=\"%(user_profile)s\">votre profil</a>."
+msgstr "Félicitations, vous avez reçu un badge '%(badge_name)s'. Consultez <a href=\"%(user_profile)s\">votre profil</a>."
#: models/__init__.py:3657
#, python-format
@@ -4572,12 +4572,12 @@ msgstr "%(name)s"
#: templates/badge.html:5
msgid "Badge"
-msgstr "Médaille"
+msgstr "Badge"
#: templates/badge.html:7
#, python-format
msgid "Badge \"%(name)s\""
-msgstr "Médaille \"%(name)s\""
+msgstr "Badge \"%(name)s\""
#: templates/badge.html:9 templates/user_profile/user_recent.html:16
#: templates/user_profile/user_stats.html:71
@@ -4588,12 +4588,12 @@ msgstr "%(description)s"
#: templates/badge.html:14
msgid "user received this badge:"
msgid_plural "users received this badge:"
-msgstr[0] "utilisateur a reçu cette médaille :"
-msgstr[1] "utilisateurs ont reçu cette médaille :"
+msgstr[0] "utilisateur a reçu ce badge :"
+msgstr[1] "utilisateurs ont reçu ce badge :"
#: templates/badges.html:3 templates/badges.html.py:5
msgid "Badges"
-msgstr "Médailles"
+msgstr "Badges"
#: templates/badges.html:7
msgid "Community gives you awards for your questions, answers and votes."
@@ -4603,11 +4603,11 @@ msgstr "La communauté récompense vos questions, vos réponses et vos votes en
msgid ""
"Below is the list of available badges and number of times each type of badge"
" has been awarded."
-msgstr "Ci-dessous figure la liste des médailles existantes et le nombre de fois où elles ont été décernées."
+msgstr "Ci-dessous figure la liste des badges existants et le nombre de fois où elles ont été décernées."
#: templates/badges.html:31
msgid "Community badges"
-msgstr "Niveaux de médailles"
+msgstr "Niveaux de badges"
#: templates/badges.html:33
msgid "gold badge: the highest honor and is very rare"
@@ -4617,7 +4617,7 @@ msgstr "badge en or: le plus haute distinction et est très rare"
msgid ""
"Gold badge is the highest award in this community. To obtain it you have to show \n"
"profound knowledge and ability in addition to your active participation."
-msgstr "La médaille d'or est la plus haute distinction dans cette communauté. Pour l'obtenir, vous devez montrer une profonde connaissance et de grandes capacités en plus de votre participation active."
+msgstr "Le badge d'or est la plus haute distinction dans cette communauté. Pour l'obtenir, vous devez montrer une profonde connaissance et de grandes capacités en plus de votre participation active."
#: templates/badges.html:42 templates/badges.html.py:46
msgid ""
@@ -4626,7 +4626,7 @@ msgstr "badge en argent: occasionnellement donné pour des contributions de trè
#: templates/badges.html:49 templates/badges.html.py:53
msgid "bronze badge: often given as a special honor"
-msgstr "médaille de bronze : souvent donnée en tant qu'honneur spécial"
+msgstr "badge de bronze : souvent donnée en tant qu'honneur spécial"
#: templates/base.html:23
#, python-format
@@ -7216,7 +7216,7 @@ msgstr[1] "<span class=\"count\">%(counter)s</span> Mots-clés"
msgid "<span class=\"count\">%(counter)s</span> Badge"
msgid_plural "<span class=\"count\">%(counter)s</span> Badges"
msgstr[0] "<span class=\"count\">%(counter)s</span> Badge"
-msgstr[1] "<span class=\"count\">%(counter)s</span> Médailles"
+msgstr[1] "<span class=\"count\">%(counter)s</span> Badges"
#: templates/user_profile/user_stats.html:85
msgid "Answer to:"
@@ -7482,7 +7482,7 @@ msgstr "Communauté"
#: templates/widgets/meta_nav.html:27
msgid "badges"
-msgstr "médailles"
+msgstr "badges"
#: templates/widgets/question_edit_tips.html:5
msgid "ask a question interesting to this community"
@@ -7581,7 +7581,7 @@ msgstr "aura :"
#: templates/widgets/user_long_score_and_badge_summary.html:15
msgid "badges:"
-msgstr "médailles :"
+msgstr "badges :"
#: templates/widgets/user_navigation.html:17
msgid "sign out"
diff --git a/askbot/locale/zh_CN/LC_MESSAGES/django.mo b/askbot/locale/zh_CN/LC_MESSAGES/django.mo
index 8d468276..6fb17c0e 100644
--- a/askbot/locale/zh_CN/LC_MESSAGES/django.mo
+++ b/askbot/locale/zh_CN/LC_MESSAGES/django.mo
Binary files differ
diff --git a/askbot/locale/zh_CN/LC_MESSAGES/django.po b/askbot/locale/zh_CN/LC_MESSAGES/django.po
index 8ed703d0..40f0106c 100644
--- a/askbot/locale/zh_CN/LC_MESSAGES/django.po
+++ b/askbot/locale/zh_CN/LC_MESSAGES/django.po
@@ -4504,7 +4504,8 @@ msgstr "发起问题"
msgid ""
"since you are not logged in right now, you will be asked to sign in or "
"register after posting your question"
-msgstr ""
+msgstr "因为你还没有登录, 发表问题后你需要注册或登录."
+
#: templates/ask.html:25
#, python-format
diff --git a/askbot/management/commands/add_admin.py b/askbot/management/commands/add_admin.py
index 6f7c7034..3f8f7fea 100644
--- a/askbot/management/commands/add_admin.py
+++ b/askbot/management/commands/add_admin.py
@@ -41,5 +41,4 @@ class Command(NoArgsCommand):
self.confirm_action()
self.remove_signals()
- self.user.set_admin_status()
- self.user.save()
+ self.user.set_status('d')
diff --git a/askbot/management/commands/askbot_add_osqa_content.py b/askbot/management/commands/askbot_add_osqa_content.py
index 6820a036..acea0391 100644
--- a/askbot/management/commands/askbot_add_osqa_content.py
+++ b/askbot/management/commands/askbot_add_osqa_content.py
@@ -311,6 +311,7 @@ class Command(BaseImportXMLCommand):
for osqa_thread in self.get_objects_for_model('forum.question'):
count += 1
#todo: there must be code lated to set the commented values
+ lang = django_settings.LANGUAGE_CODE
thread = Thread(
title=osqa_thread.title,
tagnames=osqa_thread.tagnames,
@@ -319,7 +320,7 @@ class Command(BaseImportXMLCommand):
#answer_count=thread.answer_count,
last_activity_at=osqa_thread.last_activity_at,
last_activity_by=self.get_imported_object_by_old_id(User, osqa_thread.last_activity_by),
- language_code=django_settings.LANGUAGE_CODE,
+ language_code=lang,
#"closed" data is stored differently in OSQA
#closed_by=self.get_imported_object_by_old_id(User, thread.closed_by_id),
#closed=thread.closed,
@@ -339,7 +340,7 @@ class Command(BaseImportXMLCommand):
tag_filter = Q(name__iexact=tag_names[0])
for tag_name in tag_names[1:]:
tag_filter |= Q(name__iexact=tag_name)
- tags = Tag.objects.filter(tag_filter)
+ tags = Tag.objects.filter(tag_filter & Q(language_code=lang))
thread.tagnames = ' '.join([tag.name for tag in tags])
diff --git a/askbot/management/commands/askbot_add_test_content.py b/askbot/management/commands/askbot_add_test_content.py
index a09fb086..5e5d4254 100644
--- a/askbot/management/commands/askbot_add_test_content.py
+++ b/askbot/management/commands/askbot_add_test_content.py
@@ -61,19 +61,24 @@ class Command(NoArgsCommand):
)
)
- def save_alert_settings(self):
+ def backup_settings(self):
settings = {}
for key in ALERT_SETTINGS_KEYS:
settings[key] = getattr(askbot_settings, key)
self.alert_settings = settings
+ self.limit_on_answer_setting = askbot_settings.LIMIT_ONE_ANSWER_PER_USER
- def stop_alerts(self):
+
+ def modify_settings(self):
for key in ALERT_SETTINGS_KEYS:
askbot_settings.update(key, 'n')
+ askbot_settings.update('LIMIT_ONE_ANSWER_PER_USER', False)
- def restore_saved_alert_settings(self):
+ def restore_settings(self):
for key in ALERT_SETTINGS_KEYS:
askbot_settings.update(key, self.alert_settings[key])
+ value = self.limit_on_answer_setting
+ askbot_settings.update('LIMIT_ONE_ANSWER_PER_USER', value)
def print_if_verbose(self, text):
"Only print if user chooses verbose output"
@@ -121,7 +126,7 @@ class Command(NoArgsCommand):
last_vote = False
# Each user posts a question
for i in range(NUM_QUESTIONS):
- user = users[i]
+ user = users[i % len(users)]#allows to post many questions all by less users
# Downvote/upvote the questions - It's reproducible, yet
# gives good randomized data
if not active_question is None:
@@ -164,7 +169,8 @@ class Command(NoArgsCommand):
active_answer = None
last_vote = False
# Now, fill the last added question with answers
- for user in users[:NUM_ANSWERS]:
+ for i in range(NUM_ANSWERS):
+ user = users[i % len(users)]
# We don't need to test for data validation, so ONLY users
# that aren't authors can post answer to the question
if not active_question.author is user:
@@ -213,7 +219,8 @@ class Command(NoArgsCommand):
active_question_comment = None
active_answer_comment = None
- for user in users[:NUM_COMMENTS]:
+ for i in range(NUM_COMMENTS):
+ user = users[i % len(users)]
active_question_comment = user.post_comment(
parent_post = active_question,
body_text = COMMENT_TEMPLATE
@@ -242,6 +249,8 @@ class Command(NoArgsCommand):
self.verbosity = int(options.get("verbosity", 1))
self.interactive = options.get("interactive")
+ # post a bunch of answers by admin now - that active_question is
+ # posted by someone else
if self.interactive:
answer = choice_dialog("This command will DELETE ALL DATA in the current database, and will fill the database with test data. Are you absolutely sure you want to proceed?",
choices = ("yes", "no", ))
@@ -249,16 +258,21 @@ class Command(NoArgsCommand):
return
translation.activate(django_settings.LANGUAGE_CODE)
-
- self.save_alert_settings()
- self.stop_alerts()# saves time on running the command
+ self.backup_settings()
+ self.modify_settings()# saves time on running the command
# Create Users
users = self.create_users()
- # Create Questions, vote for questions
+ # Create a bunch of questions and answers by a single user
+ # to test pagination in the user profile
+ active_question = self.create_questions(users[0:1])
+
+ # Create Questions, vote for questions by all other users
active_question = self.create_questions(users)
+ active_answer = self.create_answers(users[0:1], active_question)
+
# Create Answers, vote for the answers, vote for the active question
# vote for the active answer
active_answer = self.create_answers(users, active_question)
@@ -302,7 +316,5 @@ class Command(NoArgsCommand):
force = True,
)
self.print_if_verbose("User has accepted a best answer")
-
- self.restore_saved_alert_settings()
-
+ self.restore_settings()
self.print_if_verbose("DONE")
diff --git a/askbot/management/commands/askbot_add_xml_content.py b/askbot/management/commands/askbot_add_xml_content.py
index 307a0d76..84082253 100644
--- a/askbot/management/commands/askbot_add_xml_content.py
+++ b/askbot/management/commands/askbot_add_xml_content.py
@@ -352,7 +352,7 @@ class Command(BaseImportXMLCommand):
old_tag_id = tag.id
try:
#try to get existing tag with this name
- tag = Tag.objects.get(name__iexact=tag.name)
+ tag = Tag.objects.get(name__iexact=tag.name, language_code=tag.language_code)
except Tag.DoesNotExist:
tag.id = None
tag.tag_wiki = None
@@ -392,7 +392,7 @@ class Command(BaseImportXMLCommand):
tag_filter = Q(name__iexact=tag_names[0])
for tag_name in tag_names[1:]:
tag_filter |= Q(name__iexact=tag_name)
- tags = Tag.objects.filter(tag_filter)
+ tags = Tag.objects.filter(tag_filter & Q(language_code=thread.language_code))
new_thread.tagnames = ' '.join([tag.name for tag in tags])
diff --git a/askbot/management/commands/create_tag_synonyms.py b/askbot/management/commands/create_tag_synonyms.py
index e4324639..167beb9a 100644
--- a/askbot/management/commands/create_tag_synonyms.py
+++ b/askbot/management/commands/create_tag_synonyms.py
@@ -4,6 +4,7 @@ all corresponding questions are retagged
import sys
from optparse import make_option
+from django.conf import settings as django_settings
from django.core import management
from django.core.management.base import BaseCommand, CommandError
from askbot import models
@@ -47,6 +48,13 @@ remove source_tag"""
default = None,
help = 'id of the user who will be marked as a performer of this operation'
),
+ make_option('--lang',
+ action='store',
+ type='str',
+ dest='lang',
+ default=django_settings.LANGUAGE_CODE,
+ help='language code of the tag, e.g. "en"'
+ )
)
def handle(self, *args, **options):
@@ -75,7 +83,7 @@ remove source_tag"""
is_source_tag_created = False
try:
- source_tag = models.Tag.objects.get(name=source_tag_name)
+ source_tag = models.Tag.objects.get(name=source_tag_name, language_code=options['lang'])
except models.Tag.DoesNotExist:
if not options.get('is_force', False):
prompt = """source tag %s doesn't exist, are you sure you want to create a TagSynonym
@@ -84,16 +92,21 @@ remove source_tag"""
if choice == 'no':
print 'Cancled'
sys.exit()
- source_tag = models.Tag.objects.create(name=source_tag_name,
- created_by=admin
- )
+ source_tag = models.Tag.objects.create(
+ name=source_tag_name,
+ created_by=admin,
+ language_code=options['lang']
+ )
is_source_tag_created = True
# test if target_tag is actually synonym for yet another tag
# when user asked tag2->tag3, we already had tag3->tag4.
try:
- tag_synonym_tmp = models.TagSynonym.objects.get(source_tag_name = target_tag_name)
+ tag_synonym_tmp = models.TagSynonym.objects.get(
+ source_tag_name=target_tag_name,
+ language_code=options['lang']
+ )
if not options.get('is_force', False):
prompt = """There exists a TagSynonym %s ==> %s,
hence we will create a tag synonym %s ==> %s instead. Proceed?""" % (tag_synonym_tmp.source_tag_name, tag_synonym_tmp.target_tag_name,
@@ -108,27 +121,34 @@ remove source_tag"""
pass
try:
- models.Tag.objects.get(name=target_tag_name)
+ models.Tag.objects.get(name=target_tag_name, language_code=options['lang'])
except models.Tag.DoesNotExist:
# we are creating a target tag, let's copy source tag's info
# used_count are updated later
- models.Tag.objects.create(name=target_tag_name,
- created_by = admin,
- status = source_tag.status,
- tag_wiki = source_tag.tag_wiki
- )
-
- tag_synonym_tmp, created = models.TagSynonym.objects.get_or_create(source_tag_name = source_tag_name,
- target_tag_name = target_tag_name,
- owned_by = admin
- )
+ models.Tag.objects.create(
+ name=target_tag_name,
+ created_by = admin,
+ status = source_tag.status,
+ tag_wiki = source_tag.tag_wiki,
+ language_code=options['lang']
+ )
+
+ tag_synonym_tmp, created = models.TagSynonym.objects.get_or_create(
+ source_tag_name=source_tag_name,
+ target_tag_name=target_tag_name,
+ owned_by=admin,
+ language_code=options['lang']
+ )
management.call_command('rename_tags', *args, **options)
# When source_tag_name is a target_tag_name of already existing TagSynonym.
# ie. if tag1->tag2 exists when user asked tag2->tag3
# we are going to convert all tag1->tag2 to tag1->tag3 as well
- existing_tag_synonyms = models.TagSynonym.objects.filter(target_tag_name=source_tag_name)
+ existing_tag_synonyms = models.TagSynonym.objects.filter(
+ target_tag_name=source_tag_name,
+ language_code=options['lang']
+ )
for existing_tag_synonym in existing_tag_synonyms:
new_options = options.copy()
new_options['from'] = existing_tag_synonym.source_tag_name
diff --git a/askbot/management/commands/create_thousand_tags.py b/askbot/management/commands/create_thousand_tags.py
index 05c77f46..45953e74 100644
--- a/askbot/management/commands/create_thousand_tags.py
+++ b/askbot/management/commands/create_thousand_tags.py
@@ -1,4 +1,5 @@
from django.core.management.base import NoArgsCommand
+from django.conf import settings as django_settings
from django.db import transaction
from askbot import models
import sys
@@ -10,9 +11,10 @@ class Command(NoArgsCommand):
for i in xrange(1000):
name = 'tag' + str(i)
models.Tag.objects.create(
- name = name,
- created_by = user
- )
+ name=name,
+ created_by=user,
+ language_code=django_settings.LANGUAGE_CODE
+ )
if i % 1000 == 0:
transaction.commit()
transaction.commit()
diff --git a/askbot/management/commands/createsuperuser.py b/askbot/management/commands/createsuperuser.py
index eb363bbd..d0faf253 100644
--- a/askbot/management/commands/createsuperuser.py
+++ b/askbot/management/commands/createsuperuser.py
@@ -100,8 +100,7 @@ class Command(Command):
self.remove_signals()
u = User.objects.create_superuser(username, email, password)
- u.set_admin_status()
- u.save()
+ u.set_status('d')
if verbosity >= 1:
self.stdout.write("Askbot Superuser created successfully.\n")
diff --git a/askbot/management/commands/fix_question_tags.py b/askbot/management/commands/fix_question_tags.py
index 3cb696b0..48168ee8 100644
--- a/askbot/management/commands/fix_question_tags.py
+++ b/askbot/management/commands/fix_question_tags.py
@@ -27,10 +27,12 @@ def get_valid_tag_name(tag):
first_char_regex = re.compile('^%s+' % const.TAG_FORBIDDEN_FIRST_CHARS)
return first_char_regex.sub('', name)
-class Command(NoArgsCommand):
- def handle_noargs(self, **options):
+class Command(Command):
+ def handle_noargs(self, *args, **options):
signal_data = signals.pop_all_db_signal_receivers()
- self.run_command()
+ languages = models.Tag.objects.values_list('language_code').distinct()
+ for lang in languages:
+ self.run_command(lang)
signals.set_all_db_signal_receivers(signal_data)
def retag_threads(self, from_tags, to_tag):
@@ -50,11 +52,13 @@ class Command(NoArgsCommand):
@transaction.commit_manually
- def run_command(self):
+ def run_command(self, lang):
"""method that runs the actual command"""
#go through tags and find character case duplicates and eliminate them
- translation.activate(django_settings.LANGUAGE_CODE)
- tagnames = models.Tag.objects.values_list('name', flat = True)
+ translation.activate(lang)
+ tagnames = models.Tag.objects.filter(
+ language_code=lang
+ ).values_list('name', flat=True)
self.admin = get_admin()
#1) first we go through all tags and
@@ -63,7 +67,10 @@ class Command(NoArgsCommand):
for name in tagnames:
try:
- tag = models.Tag.objects.get(name=name)
+ tag = models.Tag.objects.get(
+ name=name,
+ language_code=lang
+ )
except models.Tag.DoesNotExist:
#tag with this name was already deleted,
#because it was an invalid duplicate version
@@ -89,7 +96,10 @@ class Command(NoArgsCommand):
#then delete the current tag as no longer used
if fixed_name != name:
try:
- duplicate_tag = models.Tag.objects.get(name=fixed_name)
+ duplicate_tag = models.Tag.objects.get(
+ name=fixed_name,
+ language_code=lang
+ )
except models.Tag.DoesNotExist:
pass
self.retag_threads([tag], duplicate_tag)
@@ -102,7 +112,8 @@ class Command(NoArgsCommand):
#from the case variants to the current tag and
#delete the case variant tags
dupes = models.Tag.objects.filter(
- name__iexact=fixed_name
+ name__iexact=fixed_name,
+ language_code=lang
).exclude(pk=tag.id)
dupes_count = dupes.count()
@@ -135,7 +146,8 @@ class Command(NoArgsCommand):
denorm_tag_set.update(norm_tag_set)
cleaned_tag_set = set(
models.Tag.objects.filter(
- name__in=denorm_tag_set
+ name__in=denorm_tag_set,
+ language_code=lang
).values_list('name', flat=True)
)
self.admin.retag_question(
diff --git a/askbot/management/commands/generate_post_snippets.py b/askbot/management/commands/generate_post_snippets.py
index 688450bf..0f45c3da 100644
--- a/askbot/management/commands/generate_post_snippets.py
+++ b/askbot/management/commands/generate_post_snippets.py
@@ -13,6 +13,9 @@ class Command(NoArgsCommand):
for post in ProgressBar(posts.iterator(), count, message):
if hasattr(post, 'summary'):
post.summary = post.get_snippet()
+ post.html = post.parse_post_text()['html']
post.save()
transaction.commit()
+ if post.thread:
+ post.thread.invalidate_cached_data(lazy=True)
transaction.commit()
diff --git a/askbot/management/commands/remove_admin.py b/askbot/management/commands/remove_admin.py
index 2aa95c20..eca0b7b6 100644
--- a/askbot/management/commands/remove_admin.py
+++ b/askbot/management/commands/remove_admin.py
@@ -41,5 +41,4 @@ class Command(NoArgsCommand):
self.confirm_action()
self.remove_signals()
- self.user.remove_admin_status()
- self.user.save()
+ self.user.set_status('a')
diff --git a/askbot/management/commands/rename_tags.py b/askbot/management/commands/rename_tags.py
index b3eaf70d..a4e49a34 100644
--- a/askbot/management/commands/rename_tags.py
+++ b/askbot/management/commands/rename_tags.py
@@ -83,6 +83,14 @@ ask you to confirm your action before making changes.
default = None,
help = 'id of the user who will be marked as a performer of this operation'
),
+ make_option('--lang',
+ action = 'store',
+ type = 'str',
+ dest = 'lang',
+ default = django_settings.LANGUAGE_CODE,
+ help = 'language code for the tags to rename e.g. "en"'
+ ),
+
)
#@transaction.commit_manually
@@ -116,7 +124,11 @@ ask you to confirm your action before making changes.
from_tags = list()
try:
for tag_name in from_tag_names:
- from_tags.append(models.Tag.objects.get(name = tag_name))
+ tag = models.Tag.objects.get(
+ name=tag_name,
+ language_code=options['lang']
+ )
+ from_tags.append(tag)
except models.Tag.DoesNotExist:
error_message = u"""tag %s was not found. It is possible that the tag
exists but we were not able to match it's unicode value
@@ -134,12 +146,17 @@ Also, you can try command "rename_tag_id"
to_tags = list()
for tag_name in to_tag_names:
try:
- to_tags.append(models.Tag.objects.get(name = tag_name))
+ tag = models.Tag.objects.get(
+ name=tag_name,
+ language_code=options['lang']
+ )
+ to_tags.append(tag)
except models.Tag.DoesNotExist:
to_tags.append(
models.Tag.objects.create(
- name = tag_name,
- created_by = admin
+ name=tag_name,
+ created_by=admin,
+ language_code=options['lang']
)
)
except models.Tag.MultipleObjectsReturned:
diff --git a/askbot/management/commands/rename_tags_id.py b/askbot/management/commands/rename_tags_id.py
index 45e4a76a..7926d28d 100644
--- a/askbot/management/commands/rename_tags_id.py
+++ b/askbot/management/commands/rename_tags_id.py
@@ -104,6 +104,14 @@ rename_tags, but using tag id's
from_tags = get_tags_by_ids(from_tag_ids)
to_tags = get_tags_by_ids(to_tag_ids)
+
+ #all tags must belong to the same language
+ lang_codes = {tag.language_code for tag in (from_tags + to_tags)}
+ if len(lang_codes) != 1:
+ langs = ', '.join(lang_codes)
+ raise CommandError('all tags must belong to the same language, have: %s' % langs)
+ lang = list(lang_codes).pop()
+
admin = get_admin(options['user_id'])
questions = models.Thread.objects.all()
@@ -145,7 +153,10 @@ or repost a bug, if that does not help"""
#if user provided tag1 as to_tag, and tagsynonym tag1->tag2 exists.
for to_tag_name in to_tag_names:
try:
- tag_synonym = models.TagSynonym.objects.get(source_tag_name = to_tag_name)
+ tag_synonym = models.TagSynonym.objects.get(
+ source_tag_name=to_tag_name,
+ language_code=lang
+ )
raise CommandError(u'You gave %s as --to argument, but TagSynonym: %s -> %s exists, probably you want to provide %s as --to argument' % (to_tag_name, tag_synonym.source_tag_name, tag_synonym.target_tag_name, tag_synonym.target_tag_name))
except models.TagSynonym.DoesNotExist:
pass
@@ -195,4 +206,7 @@ or repost a bug, if that does not help"""
# we want to update tagsynonym (tag1->tag2) to (tag1->tag3)
for from_tag_name in from_tag_names:
# we need db_index for target_tag_name as well for this
- models.TagSynonym.objects.filter(target_tag_name = from_tag_name).update(target_tag_name = to_tag_name)
+ models.TagSynonym.objects.filter(
+ target_tag_name=from_tag_name,
+ language_code=lang
+ ).update(target_tag_name = to_tag_name)
diff --git a/askbot/management/commands/send_email_alerts.py b/askbot/management/commands/send_email_alerts.py
index a9162079..1f04690f 100644
--- a/askbot/management/commands/send_email_alerts.py
+++ b/askbot/management/commands/send_email_alerts.py
@@ -432,14 +432,23 @@ class Command(NoArgsCommand):
question_count = len(q_list.keys())
- subject_line = ungettext(
- '%(question_count)d update about %(topics)s',
- '%(question_count)d updates about %(topics)s',
- question_count
- ) % {
- 'question_count': question_count,
- 'topics': tag_summary
- }
+ if tag_summary:
+ subject_line = ungettext(
+ '%(question_count)d update about %(topics)s',
+ '%(question_count)d updates about %(topics)s',
+ question_count
+ ) % {
+ 'question_count': question_count,
+ 'topics': tag_summary
+ }
+ else:
+ subject_line = ungettext(
+ '%(question_count)d update',
+ '%(question_count)d updates',
+ question_count
+ ) % {
+ 'question_count': question_count,
+ }
items_added = 0
items_unreported = 0
@@ -471,7 +480,7 @@ class Command(NoArgsCommand):
'name': user.username,
'admin_email': askbot_settings.ADMIN_EMAIL,
'site_name': askbot_settings.APP_SHORT_NAME,
- 'is_multilingual': django_settings.ASKBOT_MULTILINGUAL
+ 'is_multilingual': getattr(django_settings, 'ASKBOT_MULTILINGUAL', False)
})
if DEBUG_THIS_COMMAND == True:
diff --git a/askbot/media/jquery-openid/images/mozilla-persona.gif b/askbot/media/jquery-openid/images/mozilla-persona.gif
new file mode 100644
index 00000000..e4a8a2d4
--- /dev/null
+++ b/askbot/media/jquery-openid/images/mozilla-persona.gif
Binary files differ
diff --git a/askbot/media/jquery-openid/jquery.openid.js b/askbot/media/jquery-openid/jquery.openid.js
index 20807065..dac4d297 100644
--- a/askbot/media/jquery-openid/jquery.openid.js
+++ b/askbot/media/jquery-openid/jquery.openid.js
@@ -74,7 +74,7 @@ $.fn.authenticator = function() {
if (confirm(message)){
$.ajax({
type: 'POST',
- url: authUrl + 'delete_login_method/',//url!!!
+ url: askbot['urls']['deleteLoginMethod'],
data: {provider_name: provider_name},
success: function(data, text_status, xhr){
$(provider_row).remove();
@@ -381,12 +381,35 @@ $.fn.authenticator = function() {
$('#id_email').focus();
};
+ var start_mozilla_persona_login = function() {
+ navigator.id.request();
+ return false;
+ };
+
var clear_password_fields = function(){
$('#id_password').val('');
$('#id_new_password').val('');
$('#id_new_password_retyped').val('');
};
+ var setupMozillaPersonaListeners = function() {
+ navigator.id.watch({
+ loggedInUser: askbot['data']['userEmail'],
+ onlogin: function(assertion) {
+ var assertionElement = signin_form.find('input[name=persona_assertion]');
+ assertionElement.val(assertion);
+ provider_name_input.val('mozilla-persona');
+ signin_form.submit();
+ return false;
+ },
+ onlogout: function() {
+ if (askbot['data']['userIsAuthenticated']) {
+ window.location.href = askbot['urls']['signOut'];
+ }
+ }
+ });
+ };
+
var setup_default_handlers = function(){
setup_event_handlers(
signin_page.find('input.openid-direct'),
@@ -403,6 +426,17 @@ $.fn.authenticator = function() {
start_login_with_extra_openid_token
);
+ var mozillaPersonaBtn = signin_page.find('input.mozilla-persona');
+
+ if (mozillaPersonaBtn.length) {
+ setupMozillaPersonaListeners();
+ setup_event_handlers(
+ signin_page.find('input.mozilla-persona'),
+ start_mozilla_persona_login
+ );
+ }
+
+
setup_event_handlers(
signin_page.find('input.oauth,input.oauth2'),
start_simple_login
diff --git a/askbot/media/js/group_messaging.js b/askbot/media/js/group_messaging.js
index add50425..58572e2d 100644
--- a/askbot/media/js/group_messaging.js
+++ b/askbot/media/js/group_messaging.js
@@ -674,10 +674,10 @@ MessageCenter.prototype.hitThreadsList = function(url, senderId, requestMethod)
me.setThreadsList(threads);
me.setState('show-list');
me.setLoadingStatus(false);
- },
+ }
+ },
error: function() {
me.setLoadingStatus(false);
- }
}
});
this.setLoadingStatus(true);
diff --git a/askbot/media/js/less.min.js b/askbot/media/js/less.min.js
index 6e4d5cff..77ac4fca 100644
--- a/askbot/media/js/less.min.js
+++ b/askbot/media/js/less.min.js
@@ -1,16 +1,16 @@
-//
-// LESS - Leaner CSS v1.1.3
-// http://lesscss.org
-//
-// Copyright (c) 2009-2011, Alexis Sellier
-// Licensed under the Apache 2.0 License.
-//
-//
-// LESS - Leaner CSS v1.1.3
-// http://lesscss.org
-//
-// Copyright (c) 2009-2011, Alexis Sellier
-// Licensed under the Apache 2.0 License.
-//
-(function(a,b){function v(a,b){var c="less-error-message:"+p(b),e=["<ul>",'<li><label>[-1]</label><pre class="ctx">{0}</pre></li>',"<li><label>[0]</label><pre>{current}</pre></li>",'<li><label>[1]</label><pre class="ctx">{2}</pre></li>',"</ul>"].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="<h3>"+(a.message||"There is an error in your .less file")+"</h3>"+'<p><a href="'+b+'">'+b+"</a> ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":</p>"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+'<span class="error">'+a.extract[1].slice(a.column)+"</span>")),f.innerHTML=h,q([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}function u(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function t(a){return a&&a.parentNode.removeChild(a)}function s(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){u("browser doesn't support AJAX.");return null}}function r(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=s(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function q(a,b,c){var d,e=b.href?b.href.replace(/\?.*$/,""):"",f="less:"+(b.title||p(e));(d=document.getElementById(f))===null&&(d=document.createElement("style"),d.type="text/css",d.media=b.media||"screen",d.id=f,document.getElementsByTagName("head")[0].appendChild(d));if(d.styleSheet)try{d.styleSheet.cssText=a}catch(g){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(a){d.childNodes.length>0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(u("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function p(a){return a.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function o(b,c,e,f){var g=a.location.href.replace(/[#?].*$/,""),i=b.href.replace(/\?.*$/,""),j=h&&h.getItem(i),k=h&&h.getItem(i+":timestamp"),l={css:j,timestamp:k};/^(https?|file):/.test(i)||(i.charAt(0)=="/"?i=a.location.protocol+"//"+a.location.host+i:i=g.slice(0,g.lastIndexOf("/")+1)+i),r(b.href,b.type,function(a,g){if(!e&&l&&g&&(new Date(g)).valueOf()===(new Date(l.timestamp)).valueOf())q(l.css,b),c(null,b,{local:!0,remaining:f});else try{(new d.Parser({optimization:d.optimization,paths:[i.replace(/[\w\.-]+$/,"")],mime:b.type})).parse(a,function(a,d){if(a)return v(a,i);try{c(d,b,{local:!1,lastModified:g,remaining:f}),t(document.getElementById("less-error-message:"+p(i)))}catch(a){v(a,i)}})}catch(h){v(h,i)}},function(a,b){throw new Error("Couldn't load "+b+" ("+a+")")})}function n(a,b){for(var c=0;c<d.sheets.length;c++)o(d.sheets[c],a,b,d.sheets.length-(c+1))}function m(){var a=document.getElementsByTagName("style");for(var b=0;b<a.length;b++)a[b].type.match(k)&&(new d.Parser).parse(a[b].innerHTML||"",function(c,d){a[b].type="text/css",a[b].innerHTML=d.toCSS()})}function c(b){return a.less[b.split("/")[1]]}Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d<c;d++)d in this&&a.call(b,this[d],d,this)}),Array.prototype.map||(Array.prototype.map=function(a){var b=this.length>>>0,c=Array(b),d=arguments[1];for(var e=0;e<b;e++)e in this&&(c[e]=a.call(d,this[e],e,this));return c}),Array.prototype.filter||(Array.prototype.filter=function(a){var b=[],c=arguments[1];for(var d=0;d<this.length;d++)a.call(c,this[d])&&b.push(this[d]);return b}),Array.prototype.reduce||(Array.prototype.reduce=function(a){var b=this.length>>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else for(;;){if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}for(;c<b;c++)c in this&&(d=a.call(null,d,this[c],c,this));return d}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){var b=this.length,c=arguments[1]||0;if(!b)return-1;if(c>=b)return-1;c<0&&(c+=b);for(;c<b;c++){if(!Object.prototype.hasOwnProperty.call(this,c))continue;if(a===this[c])return c}return-1}),Object.keys||(Object.keys=function(a){var b=[];for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&b.push(c);return b}),String.prototype.trim||(String.prototype.trim=function(){return String(this).replace(/^\s\s*/,"").replace(/\s\s*$/,"")});var d,e;typeof a=="undefined"?(d=exports,e=c("less/tree")):(typeof a.less=="undefined"&&(a.less={}),d=a.less,e=a.less.tree={}),d.Parser=function(a){function t(a){return typeof a=="string"?b.charAt(c)===a:a.test(j[f])?!0:!1}function s(a){var d,e,g,h,i,m,n,o;if(a instanceof Function)return a.call(l.parsers);if(typeof a=="string")d=b.charAt(c)===a?a:null,g=1,r();else{r();if(d=a.exec(j[f]))g=d[0].length;else return null}if(d){o=c+=g,m=c+j[f].length-g;while(c<m){h=b.charCodeAt(c);if(h!==32&&h!==10&&h!==9)break;c++}j[f]=j[f].slice(g+(c-o)),k=c,j[f].length===0&&f<j.length-1&&f++;return typeof d=="string"?d:d.length===1?d[0]:d}}function r(){c>k&&(j[f]=j[f].slice(c-k),k=c)}function q(){j[f]=g,c=h,k=c}function p(){g=j[f],h=c,k=c}var b,c,f,g,h,i,j,k,l,m=this,n=function(){},o=this.imports={paths:a&&a.paths||[],queue:[],files:{},mime:a&&a.mime,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=a,c(a),e.queue.length===0&&n()},a)}};this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null;return l={imports:o,parse:function(d,g){var h,l,m,o,p,q,r=[],t,u=null;c=f=k=i=0,j=[],b=d.replace(/\r\n/g,"\n"),j=function(c){var d=0,e=/[^"'`\{\}\/\(\)]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=0,h,i=c[0],j,k;for(var l=0,m,n;l<b.length;l++){e.lastIndex=l,(h=e.exec(b))&&h.index===l&&(l+=h[0].length,i.push(h[0])),m=b.charAt(l),f.lastIndex=l,!k&&!j&&m==="/"&&(n=b.charAt(l+1),(n==="/"||n==="*")&&(h=f.exec(b))&&h.index===l&&(l+=h[0].length,i.push(h[0]),m=b.charAt(l)));if(m==="{"&&!k&&!j)g++,i.push(m);else if(m==="}"&&!k&&!j)g--,i.push(m),c[++d]=i=[];else if(m==="("&&!k&&!j)i.push(m),j=!0;else if(m===")"&&!k&&j)i.push(m),j=!1;else{if(m==='"'||m==="'"||m==="`")k?k=k===m?!1:k:k=m;i.push(m)}}if(g>0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]),h=new e.Ruleset([],s(this.parsers.primary)),h.root=!0,h.toCSS=function(c){var d,f,g;return function(g,h){function n(a){return a?(b.slice(0,a).match(/\n/g)||"").length:null}var i=[];g=g||{},typeof h=="object"&&!Array.isArray(h)&&(h=Object.keys(h).map(function(a){var b=h[a];b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b]));return new e.Rule("@"+a,b,!1,0)}),i=[new e.Ruleset(null,h)]);try{var j=c.call(this,{frames:i}).toCSS([],{compress:g.compress||!1})}catch(k){f=b.split("\n"),d=n(k.index);for(var l=k.index,m=-1;l>=0&&b.charAt(l)!=="\n";l--)m++;throw{type:k.type,message:k.message,filename:a.filename,index:k.index,line:typeof d=="number"?d+1:null,callLine:k.call&&n(k.call)+1,callExtract:f[n(k.call)],stack:k.stack,column:m,extract:[f[d-1],f[d],f[d+1]]}}return g.compress?j.replace(/(\s)+/g,"$1"):j}}(h.eval);if(c<b.length-1){c=i,q=b.split("\n"),p=(b.slice(0,c).match(/\n/g)||"").length+1;for(var v=c,w=-1;v>=0&&b.charAt(v)!=="\n";v--)w++;u={name:"ParseError",message:"Syntax Error on line "+p,index:c,filename:a.filename,line:p,column:w,extract:[q[p-2],q[p-1],q[p]]}}this.imports.queue.length>0?n=function(){g(u,h)}:g(u,h)},parsers:{primary:function(){var a,b=[];while((a=s(this.mixin.definition)||s(this.rule)||s(this.ruleset)||s(this.mixin.call)||s(this.comment)||s(this.directive))||s(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(c)==="/"){if(b.charAt(c+1)==="/")return new e.Comment(s(/^\/\/.*/),!0);if(a=s(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)}},entities:{quoted:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==='"'||b.charAt(d)==="'"){f&&s("~");if(a=s(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],f)}},keyword:function(){var a;if(a=s(/^[A-Za-z-]+/))return new e.Keyword(a)},call:function(){var a,b,d=c;if(!!(a=/^([\w-]+|%)\(/.exec(j[f]))){a=a[1].toLowerCase();if(a==="url")return null;c+=a.length;if(a==="alpha")return s(this.alpha);s("("),b=s(this.entities.arguments);if(!s(")"))return;if(a)return new e.Call(a,b,d)}},arguments:function(){var a=[],b;while(b=s(this.expression)){a.push(b);if(!s(","))break}return a},literal:function(){return s(this.entities.dimension)||s(this.entities.color)||s(this.entities.quoted)},url:function(){var a;if(b.charAt(c)==="u"&&!!s(/^url\(/)){a=s(this.entities.quoted)||s(this.entities.variable)||s(this.entities.dataURI)||s(/^[-\w%@$\/.&=:;#+?~]+/)||"";if(!s(")"))throw new Error("missing closing ) for url()");return new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),o.paths)}},dataURI:function(){var a;if(s(/^data:/)){a={},a.mime=s(/^[^\/]+\/[^,;)]+/)||"",a.charset=s(/^;\s*charset=[^,;)]+/)||"",a.base64=s(/^;\s*base64/)||"",a.data=s(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,d=c;if(b.charAt(c)==="@"&&(a=s(/^@@?[\w-]+/)))return new e.Variable(a,d)},color:function(){var a;if(b.charAt(c)==="#"&&(a=s(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,d=b.charCodeAt(c);if(!(d>57||d<45||d===47))if(a=s(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==="`"){f&&s("~");if(a=s(/^`([^`]*)`/))return new e.JavaScript(a[1],c,f)}}},variable:function(){var a;if(b.charAt(c)==="@"&&(a=s(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!!t(/^[@\w.%-]+\/[@\w.-]+/)&&(a=s(this.entity))&&s("/")&&(b=s(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var a=[],d,f,g,h=c,i=b.charAt(c);if(i==="."||i==="#"){while(d=s(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new e.Element(f,d)),f=s(">");s("(")&&(g=s(this.entities.arguments))&&s(")");if(a.length>0&&(s(";")||t("}")))return new e.mixin.Call(a,g,h)}},definition:function(){var a,d=[],f,g,h,i;if(!(b.charAt(c)!=="."&&b.charAt(c)!=="#"||t(/^[^{]*(;|})/)))if(f=s(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=f[1];while(h=s(this.entities.variable)||s(this.entities.literal)||s(this.entities.keyword)){if(h instanceof e.Variable)if(s(":"))if(i=s(this.expression))d.push({name:h.name,value:i});else throw new Error("Expected value");else d.push({name:h.name});else d.push({value:h});if(!s(","))break}if(!s(")"))throw new Error("Expected )");g=s(this.block);if(g)return new e.mixin.Definition(a,d,g)}}},entity:function(){return s(this.entities.literal)||s(this.entities.variable)||s(this.entities.url)||s(this.entities.call)||s(this.entities.keyword)||s(this.entities.javascript)||s(this.comment)},end:function(){return s(";")||t("}")},alpha:function(){var a;if(!!s(/^\(opacity=/i))if(a=s(/^\d+/)||s(this.entities.variable)){if(!s(")"))throw new Error("missing closing ) for alpha()");return new e.Alpha(a)}},element:function(){var a,b,c;c=s(this.combinator),a=s(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||s("*")||s(this.attribute)||s(/^\([^)@]+\)/);if(a)return new e.Element(c,a)},combinator:function(){var a,d=b.charAt(c);if(d===">"||d==="&"||d==="+"||d==="~"){c++;while(b.charAt(c)===" ")c++;return new e.Combinator(d)}if(d===":"&&b.charAt(c+1)===":"){c+=2;while(b.charAt(c)===" ")c++;return new e.Combinator("::")}return b.charAt(c-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,d,f=[],g,h;while(d=s(this.element)){g=b.charAt(c),f.push(d);if(g==="{"||g==="}"||g===";"||g===",")break}if(f.length>0)return new e.Selector(f)},tag:function(){return s(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||s("*")},attribute:function(){var a="",b,c,d;if(!!s("[")){if(b=s(/^[a-zA-Z-]+/)||s(this.entities.quoted))(d=s(/^[|~*$^]?=/))&&(c=s(this.entities.quoted)||s(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!s("]"))return;if(a)return"["+a+"]"}},block:function(){var a;if(s("{")&&(a=s(this.primary))&&s("}"))return a},ruleset:function(){var a=[],b,d,g;p();if(g=/^([.#: \w-]+)[\s\n]*\{/.exec(j[f]))c+=g[0].length-1,a=[new e.Selector([new e.Element(null,g[1])])];else while(b=s(this.selector)){a.push(b),s(this.comment);if(!s(","))break;s(this.comment)}if(a.length>0&&(d=s(this.block)))return new e.Ruleset(a,d);i=c,q()},rule:function(){var a,d,g=b.charAt(c),k,l;p();if(g!=="."&&g!=="#"&&g!=="&")if(a=s(this.variable)||s(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(j[f]))?(c+=l[0].length-1,d=new e.Anonymous(l[1])):a==="font"?d=s(this.font):d=s(this.value),k=s(this.important);if(d&&s(this.end))return new e.Rule(a,d,k,h);i=c,q()}},"import":function(){var a;if(s(/^@import\s+/)&&(a=s(this.entities.quoted)||s(this.entities.url))&&s(";"))return new e.Import(a,o)},directive:function(){var a,d,f,g;if(b.charAt(c)==="@"){if(d=s(this["import"]))return d;if(a=s(/^@media|@page|@-[-a-z]+/)){g=(s(/^[^{]+/)||"").trim();if(f=s(this.block))return new e.Directive(a+" "+g,f)}else if(a=s(/^@[-a-z]+/))if(a==="@font-face"){if(f=s(this.block))return new e.Directive(a,f)}else if((d=s(this.entity))&&s(";"))return new e.Directive(a,d)}},font:function(){var a=[],b=[],c,d,f,g;while(g=s(this.shorthand)||s(this.entity))b.push(g);a.push(new e.Expression(b));if(s(","))while(g=s(this.expression)){a.push(g);if(!s(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=s(this.expression)){b.push(a);if(!s(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(c)==="!")return s(/^! *important/)},sub:function(){var a;if(s("(")&&(a=s(this.expression))&&s(")"))return a},multiplication:function(){var a,b,c,d;if(a=s(this.operand)){while((c=s("/")||s("*"))&&(b=s(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,d,f,g;if(a=s(this.multiplication)){while((f=s(/^[-+]\s+/)||b.charAt(c-1)!=" "&&(s("+")||s("-")))&&(d=s(this.multiplication)))g=new e.Operation(f,[g||a,d]);return g||a}},operand:function(){var a,d=b.charAt(c+1);b.charAt(c)==="-"&&(d==="@"||d==="(")&&(a=s("-"));var f=s(this.sub)||s(this.entities.dimension)||s(this.entities.color)||s(this.entities.variable)||s(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),f]):f},expression:function(){var a,b,c=[],d;while(a=s(this.addition)||s(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=s(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}},typeof a!="undefined"&&(d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)}),function(a){function d(a){return Math.min(1,Math.max(0,a))}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){a=a<0?a+1:a>1?a-1:a;return a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();e.s+=c.value/100,e.s=d(e.s);return b(e)},desaturate:function(a,c){var e=a.toHSL();e.s-=c.value/100,e.s=d(e.s);return b(e)},lighten:function(a,c){var e=a.toHSL();e.l+=c.value/100,e.l=d(e.l);return b(e)},darken:function(a,c){var e=a.toHSL();e.l-=c.value/100,e.l=d(e.l);return b(e)},fadein:function(a,c){var e=a.toHSL();e.a+=c.value/100,e.a=d(e.a);return b(e)},fadeout:function(a,c){var e=a.toHSL();e.a-=c.value/100,e.a=d(e.a);return b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;d.h=e<0?360+e:e;return b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e<c.length;e++)d=d.replace(/%[sda]/i,function(a){var b=a.match(/s/i)?c[e].value:c[e].toCSS();return a.match(/[A-Z]$/)?encodeURIComponent(b):b});d=d.replace(/%%/g,"%");return new a.Quoted('"'+d+'"',d)},round:function(b){if(b instanceof a.Dimension)return new a.Dimension(Math.round(c(b)),b.unit);if(typeof b=="number")return Math.round(b);throw{error:"RuntimeError",message:"math functions take numbers as parameters"}}}}(c("less/tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={toCSS:function(){return"alpha(opacity="+(this.value.toCSS?this.value.toCSS():this.value)+")"},eval:function(a){this.value.eval&&(this.value=this.value.eval(a));return this}}}(c("less/tree")),function(a){a.Anonymous=function(a){this.value=a.value||a},a.Anonymous.prototype={toCSS:function(){return this.value},eval:function(){return this}}}(c("less/tree")),function(a){a.Call=function(a,b,c){this.name=a,this.args=b,this.index=c},a.Call.prototype={eval:function(b){var c=this.args.map(function(a){return a.eval(b)});if(!(this.name in a.functions))return new a.Anonymous(this.name+"("+c.map(function(a){return a.toCSS()}).join(", ")+")");try{return a.functions[this.name].apply(a.functions,c)}catch(d){throw{message:"error evaluating function `"+this.name+"`",index:this.index}}},toCSS:function(a){return this.eval(a).toCSS()}}}(c("less/tree")),function(a){a.Color=function(a,b){Array.isArray(a)?this.rgb=a:a.length==6?this.rgb=a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.length==8?(this.alpha=parseInt(a.substring(0,2),16)/255,this.rgb=a.substr(2).match(/.{2}/g).map(function(a){return parseInt(a,16)})):this.rgb=a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha=typeof b=="number"?b:1},a.Color.prototype={eval:function(){return this},toCSS:function(){return this.alpha<1?"rgba("+this.rgb.map(function(a){return Math.round(a)}).concat(this.alpha).join(", ")+")":"#"+this.rgb.map(function(a){a=Math.round(a),a=(a>255?255:a<0?0:a).toString(16);return a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b<c?6:0);break;case b:g=(c-a)/j+2;break;case c:g=(a-b)/j+4}g/=6}return{h:g*360,s:h,l:i,a:d}}}}(c("less/tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("less/tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)}}}(c("less/tree")),function(a){a.Directive=function(b,c){this.name=b,Array.isArray(c)?this.ruleset=new a.Ruleset([],c):this.value=c},a.Directive.prototype={toCSS:function(a,b){if(this.ruleset){this.ruleset.root=!0;return this.name+(b.compress?"{":" {\n ")+this.ruleset.toCSS(a,b).trim().replace(/\n/g,"\n ")+(b.compress?"}":"\n}\n")}return this.name+" "+this.value.toCSS()+";\n"},eval:function(a){a.frames.unshift(this),this.ruleset=this.ruleset&&this.ruleset.eval(a),a.frames.shift();return this},variable:function(b){return a.Ruleset.prototype.variable.call(this.ruleset,b)},find:function(){return a.Ruleset.prototype.find.apply(this.ruleset,arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.ruleset)}}}(c("less/tree")),function(a){a.Element=function(b,c){this.combinator=b instanceof a.Combinator?b:new a.Combinator(b),this.value=c.trim()},a.Element.prototype.toCSS=function(a){return this.combinator.toCSS(a||{})+this.value},a.Combinator=function(a){a===" "?this.value=" ":this.value=a?a.trim():""},a.Combinator.prototype.toCSS=function(a){return{"":""," ":" ","&":"",":":" :","::":"::","+":a.compress?"+":" + ","~":a.compress?"~":" ~ ",">":a.compress?">":" > "}[this.value]}}(c("less/tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(" ")}}}(c("less/tree")),function(a){a.Import=function(b,c){var d=this;this._path=b,b instanceof a.Quoted?this.path=/\.(le?|c)ss$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css$/.test(this.path),this.css||c.push(this.path,function(a){if(!a)throw new Error("Error parsing "+d.path);d.root=a})},a.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var c;if(this.css)return this;c=new a.Ruleset(null,this.root.rules.slice(0));for(var d=0;d<c.rules.length;d++)c.rules[d]instanceof a.Import&&Array.prototype.splice.apply(c.rules,[d,1].concat(c.rules[d].eval(b)));return c.rules}}}(c("less/tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify((new a.Variable("@"+e,d.index)).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: `"+f+"`",index:this.index}}for(var h in b.frames[0].variables())e[h.slice(1)]={value:b.frames[0].variables()[h].value,toJS:function(){return this.value.eval(b).toCSS()}};try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message+"'",index:this.index}}return typeof c=="string"?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(c("less/tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={eval:function(){return this},toCSS:function(){return this.value}}}(c("less/tree")),function(a){a.mixin={},a.mixin.Call=function(b,c,d){this.selector=new a.Selector(b),this.arguments=c,this.index=d},a.mixin.Call.prototype={eval:function(a){var b,c,d=[],e=!1;for(var f=0;f<a.frames.length;f++)if((b=a.frames[f].find(this.selector)).length>0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g<b.length;g++)if(b[g].match(c,a))try{Array.prototype.push.apply(d,b[g].eval(a,this.arguments).rules),e=!0}catch(h){throw{message:h.message,index:h.index,stack:h.stack,call:this.index}}if(e)return d;throw{message:"No matching definition was found for `"+this.selector.toCSS().trim()+"("+this.arguments.map(function(a){return a.toCSS()}).join(", ")+")`",index:this.index}}throw{message:this.selector.toCSS().trim()+" is undefined",index:this.index}}},a.mixin.Definition=function(b,c,d){this.name=b,this.selectors=[new a.Selector([new a.Element(null,b)])],this.params=c,this.arity=c.length,this.rules=d,this._lookups={},this.required=c.reduce(function(a,b){return!b.name||b.name&&!b.value?a+1:a},0),this.parent=a.Ruleset.prototype,this.frames=[]},a.mixin.Definition.prototype={toCSS:function(){return""},variable:function(a){return this.parent.variable.call(this,a)},variables:function(){return this.parent.variables.call(this)},find:function(){return this.parent.find.apply(this,arguments)},rulesets:function(){return this.parent.rulesets.apply(this)},eval:function(b,c){var d=new a.Ruleset(null,[]),e,f=[];for(var g=0,h;g<this.params.length;g++)if(this.params[g].name)if(h=c&&c[g]||this.params[g].value)d.rules.unshift(new a.Rule(this.params[g].name,h.eval(b)));else throw{message:"wrong number of arguments for "+this.name+" ("+c.length+" for "+this.arity+")"};for(var g=0;g<Math.max(this.params.length,c&&c.length);g++)f.push(c[g]||this.params[g].value);d.rules.unshift(new a.Rule("@arguments",(new a.Expression(f)).eval(b)));return(new a.Ruleset(null,this.rules.slice(0))).eval({frames:[this,d].concat(this.frames,b.frames)})},match:function(a,b){var c=a&&a.length||0,d;if(c<this.required)return!1;if(this.required>0&&c>this.params.length)return!1;d=Math.min(c,this.arity);for(var e=0;e<d;e++)if(!this.params[e].name&&a[e].eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("less/tree")),function(a){a.Operation=function(a,b){this.op=a.trim(),this.operands=b},a.Operation.prototype.eval=function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b),e;if(c instanceof a.Dimension&&d instanceof a.Color)if(this.op==="*"||this.op==="+")e=d,d=c,c=e;else throw{name:"OperationError",message:"Can't substract or divide a color from a number"};return c.operate(this.op,d)},a.operate=function(a,b,c){switch(a){case"+":return b+c;case"-":return b-c;case"*":return b*c;case"/":return b/c}}}(c("less/tree")),function(a){a.Quoted=function(a,b,c,d){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d},a.Quoted.prototype={toCSS:function(){return this.escaped?this.value:this.quote+this.value+this.quote},eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return(new a.JavaScript(e,c.index,!0)).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=(new a.Variable("@"+e,c.index)).eval(b);return f.value||f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index)}}}(c("less/tree")),function(a){a.Rule=function(b,c,d,e){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.index=e,b.charAt(0)==="@"?this.variable=!0:this.variable=!1},a.Rule.prototype.toCSS=function(a){return this.variable?"":this.name+(a.compress?":":": ")+this.value.toCSS(a)+this.important+";"},a.Rule.prototype.eval=function(b){return new a.Rule(this.name,this.value.eval(b),this.important,this.index)},a.Shorthand=function(a,b){this.a=a,this.b=b},a.Shorthand.prototype={toCSS:function(a){return this.a.toCSS(a)+"/"+this.b.toCSS(a)},eval:function(){return this}}}(c("less/tree")),function(a){a.Ruleset=function(a,b){this.selectors=a,this.rules=b,this._lookups={}},a.Ruleset.prototype={eval:function(b){var c=new a.Ruleset(this.selectors,this.rules.slice(0));c.root=this.root,b.frames.unshift(c);if(c.root)for(var d=0;d<c.rules.length;d++)c.rules[d]instanceof a.Import&&Array.prototype.splice.apply(c.rules,[d,1].concat(c.rules[d].eval(b)));for(var d=0;d<c.rules.length;d++)c.rules[d]instanceof a.mixin.Definition&&(c.rules[d].frames=b.frames.slice(0));for(var d=0;d<c.rules.length;d++)c.rules[d]instanceof a.mixin.Call&&Array.prototype.splice.apply(c.rules,[d,1].concat(c.rules[d].eval(b)));for(var d=0,e;d<c.rules.length;d++)e=c.rules[d],e instanceof a.mixin.Definition||(c.rules[d]=e.eval?e.eval(b):e);b.frames.shift();return c},match:function(a){return!a||a.length===0},variables:function(){return this._variables?this._variables:this._variables=this.rules.reduce(function(b,c){c instanceof a.Rule&&c.variable===!0&&(b[c.name]=c);return b},{})},variable:function(a){return this.variables()[a]},rulesets:function(){return this._rulesets?this._rulesets:this._rulesets=this.rules.filter(function(b){return b instanceof a.Ruleset||b instanceof a.mixin.Definition})},find:function(b,c){c=c||this;var d=[],e,f,g=b.toCSS();if(g in this._lookups)return this._lookups[g];this.rulesets().forEach(function(e){if(e!==c)for(var g=0;g<e.selectors.length;g++)if(f=b.match(e.selectors[g])){b.elements.length>1?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}});return this._lookups[g]=d},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;if(!this.root)if(b.length===0)g=this.selectors.map(function(a){return[a]});else for(var j=0;j<this.selectors.length;j++)for(var k=0;k<b.length;k++)g.push(b[k].concat([this.selectors[j]]));for(var l=0;l<this.rules.length;l++)i=this.rules[l],i.rules||i instanceof a.Directive?f.push(i.toCSS(g,c)):i instanceof a.Comment?i.silent||(this.root?f.push(i.toCSS(c)):e.push(i.toCSS(c))):i.toCSS&&!i.variable?e.push(i.toCSS(c)):i.value&&!i.variable&&e.push(i.value.toString());f=f.join(""),this.root?d.push(e.join(c.compress?"":"\n")):e.length>0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f);return d.join("")+(c.compress?"\n":"")}}}(c("less/tree")),function(a){a.Selector=function(a){this.elements=a,this.elements[0].combinator.value===""&&(this.elements[0].combinator.value=" ")},a.Selector.prototype.match=function(a){return this.elements[0].value===a.elements[0].value?!0:!1},a.Selector.prototype.toCSS=function(a){if(this._css)return this._css;return this._css=this.elements.map(function(b){return typeof b=="string"?" "+b.trim():b.toCSS(a)}).join("")}}(c("less/tree")),function(b){b.URL=function(b,c){b.data?this.attrs=b:(!/^(?:https?:\/|file:\/|data:\/)?\//.test(b.value)&&c.length>0&&typeof a!="undefined"&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("less/tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("less/tree")),function(a){a.Variable=function(a,b){this.name=a,this
-.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("less/tree")),c("less/tree").find=function(a,b){for(var c=0,d;c<a.length;c++)if(d=b.call(a,a[c]))return d;return null},c("less/tree").jsify=function(a){return Array.isArray(a.value)&&a.value.length>1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)};var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c){a&&q(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l<j.length;l++)(j[l].rel==="stylesheet/less"||j[l].rel.match(/stylesheet/)&&j[l].type.match(k))&&d.sheets.push(j[l]);d.refresh=function(a){var b,c;b=c=new Date,n(function(a,d,e){e.local?u("loading "+d.href+" from cache."):(u("parsed "+d.href+" successfully."),q(a.toCSS(),d,e.lastModified)),u("css for "+d.href+" generated in "+(new Date-c)+"ms"),e.remaining===0&&u("css generated in "+(new Date-b)+"ms"),c=new Date},a),m()},d.refreshStyles=m,d.refresh(d.env==="development")})(window) \ No newline at end of file
+/*!
+ * LESS - Leaner CSS v1.7.0
+ * http://lesscss.org
+ *
+ * Copyright (c) 2009-2014, Alexis Sellier <self@cloudhead.net>
+ * Licensed under the Apache v2 License.
+ *
+ */
+
+ /** * @license Apache v2
+ */
+
+!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"undefined"!=typeof console&&w.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,z.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");if(j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet)try{j.styleSheet.cssText=a}catch(k){throw new Error("Couldn't reassign styleSheet.cssText.")}else j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue;var l=document.getElementsByTagName("head")[0];if(null===h||i===!1){var m=b&&b.nextSibling||null;m?m.parentNode.insertBefore(j,m):l.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),c&&D){d("saving "+f+" to cache.",z.info);try{D.setItem(f,a),D.setItem(f+":timestamp",c)}catch(k){d("failed to save",z.errors)}}}function h(a){return w.postProcessor&&"function"==typeof w.postProcessor&&(a=w.postProcessor.call(a,a)||a),a}function i(a,c){var d,f,h="less-error-message:"+e(c||""),i='<li><label>{line}</label><pre class="{class}">{content}</pre></li>',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="<h3>"+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'</h3><p>in <a href="'+l+'">'+m+"</a> ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":</p><ul>"+k.join("")+"</ul>"):a.stack&&(f+="<br/>"+a.stack.split("\n").slice(1).join("<br/>")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==w.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function j(a,b){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?f(a,b):"function"==typeof w.errorReporting&&w.errorReporting("add",a,b):i(a,b)}function k(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function l(){}function m(a){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?l(a):"function"==typeof w.errorReporting&&w.errorReporting("remove",a):k(a)}function n(a){for(var b,c=document.getElementsByTagName("style"),d=0;d<c.length;d++)if(b=c[d],b.type.match(C)){var e=new w.tree.parseEnv(w),f=b.innerHTML||"";e.filename=document.location.href.replace(/#.*$/,""),(a||w.globalVars)&&(e.useFileCache=!0);var g=function(a){return function(b,c){if(b)return j(b,"inline");var d=c.toCSS(w);a.type="text/css",a.styleSheet?a.styleSheet.cssText=d:a.innerHTML=d}}(b);new w.Parser(e).parse(f,g,{globalVars:w.globalVars,modifyVars:a})}}function o(a,b){var c,d,e=/^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,f=a.match(e),g={},h=[];if(!f)throw new Error("Could not parse sheet href - '"+a+"'");if(!f[1]||f[2]){if(d=b.match(e),!d)throw new Error("Could not parse page url - '"+b+"'");f[1]=f[1]||d[1]||"",f[2]||(f[3]=d[3]+f[3])}if(f[3]){for(h=f[3].replace(/\\/g,"/").split("/"),c=0;c<h.length;c++)"."===h[c]&&(h.splice(c,1),c-=1);for(c=0;c<h.length;c++)".."===h[c]&&c>0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function p(a,b){var c,d,e,f,g=o(a),h=o(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c<f.length-1;c++)i+="../";for(c=0;c<e.length-1;c++)i+=e[c]+"/";return i}function q(){if(a.XMLHttpRequest&&("file:"!==a.location.protocol||!a.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(b){return d("browser doesn't support AJAX.",z.errors),null}}function r(a,b,c,e){function f(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=q(),h=y?w.fileAsync:w.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",z.debug),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),y&&!w.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function s(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=o(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=w.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?o(e.rootpath+p(f.path,h.entryPath)).path:f.path),e.useFileCache&&E[g])try{var i=E[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else r(g,e.mime,function(a,b){E[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function t(a,b,c,d,e){var f=new w.tree.parseEnv(w);f.mime=a.type,(e||w.globalVars)&&(f.useFileCache=!0),s(a.href,null,function(h,i,j,k,l){if(l){l.remaining=d;var n=D&&D.getItem(j),o=D&&D.getItem(j+":timestamp");if(!c&&o&&l.lastModified&&new Date(l.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),l.local=!0,void b(null,null,i,a,l,j)}m(j),i?(f.currentFileInfo=k,new w.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,l,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:w.globalVars})):b(h,null,null,a,l,j)},f,e)}function u(a,b,c){for(var d=0;d<w.sheets.length;d++)t(w.sheets[d],a,b,w.sheets.length-(d+1),c)}function v(){"development"===w.env?(w.optimization=0,w.watchTimer=setInterval(function(){w.watchMode&&u(function(a,b,c,d,e){if(a)j(a,d.href);else if(b){var f=b.toCSS(w);f=h(f),g(f,d,e.lastModified)}})},w.poll)):w.optimization=3}("undefined"==typeof a.less||"undefined"!=typeof a.less.nodeType)&&(a.less={}),w=a.less,x=a.less.tree={},w.mode="browser";var w,x;w===b&&(w=exports,x=c("./tree"),w.mode="node"),w.Parser=function(a){function d(){D=y,G.push({current:C,i:y,j:z})}function e(){var a=G.pop();C=a.current,D=y=a.i,z=a.j}function f(){G.pop()}function g(){y>D&&(C=C.slice(y-D),D=y)}function h(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function i(a){var b,c,d=typeof a;return"string"===d?v.charAt(y)!==a?null:(l(1),a):(g(),(b=a.exec(C))?(c=b[0].length,l(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function j(a){y>D&&(C=C.slice(y-D),D=y);var b=a.exec(C);return b?(l(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function k(a){return v.charAt(y)!==a?null:(l(1),a)}function l(a){for(var b,c=y,d=z,e=y-D,f=y+C.length-e,g=y+=a,h=v;f>y&&(b=h.charCodeAt(y),!(b>32))&&(32===b||10===b||9===b||13===b);y++);return C=C.slice(a+y-g+e),D=y,!C.length&&z<B.length-1?(C=B[++z],l(0),!0):c!==y||d!==z}function m(a,b){var c="[object Function]"===Object.prototype.toString.call(a)?a.call(F):i(a);return c?c:void o(b||("string"==typeof a?"expected '"+a+"' got '"+v.charAt(y)+"'":"unexpected token"))}function n(a,b){return v.charAt(y)===a?(l(1),a):void o(b||"expected '"+a+"' got '"+v.charAt(y)+"'")}function o(a,b){var c=new Error(a);throw c.index=y,c.type=b||"Syntax",c}function p(a){return"string"==typeof a?v.charAt(y)===a:a.test(C)}function q(a){return v.charAt(y)===a}function r(a,b){return a.filename&&b.currentFileInfo.filename&&a.filename!==b.currentFileInfo.filename?E.imports.contents[a.filename]:v}function s(a,b){for(var c=a+1,d=null,e=-1;--c>=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function t(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==w.mode&&"rhino"!==w.mode&&(e=c("path").resolve(e)),{lineNumber:s(a,b).line+1,fileName:e}}function u(a,b){var c=r(a,b),d=s(a.index,c),e=d.line,f=d.column,g=a.call&&s(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var v,y,z,A,B,C,D,E,F,G=[],H=a&&a.filename;a instanceof x.parseEnv||(a=new x.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};w.Parser.importer?w.Parser.importer(b,c,g,a):w.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return void g(b);var i=new x.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new w.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=j;return u.prototype=new Error,u.prototype.constructor=u,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,E={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(y=z=D=A=0,j=f&&f.globalVars?w.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+w.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,E.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),v=d=m+d.replace(/^\uFEFF/,"")+k,E.imports.contents[a.currentFileInfo.filename]=d,B=function(b){function c(b,c){l=new u({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||q||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new u(l,a));C=B[0];try{g=new x.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new u(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new x.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof x.Value||(b instanceof x.Expression||(b=new x.Expression([b])),b=new x.Value([b])),new x.Rule("@"+a,b,!1,null,0)}),i.frames=[new x.Ruleset(null,f)]);try{var j,k=[],l=[new x.joinSelectorVisitor,new x.processExtendsVisitor,new x.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j<e.plugins.length;j++)e.plugins[j].isPreEvalVisitor?k.push(e.plugins[j]):e.plugins[j].isPreVisitor?l.splice(0,0,e.plugins[j]):l.push(e.plugins[j]);for(j=0;j<k.length;j++)k[j].run(m);for(g=d.call(m,i),j=0;j<l.length;j++)l[j].run(g);e.sourceMap&&(g=new x.sourceMapOutput({contentsIgnoredCharsMap:E.imports.contentsIgnoredChars,writeSourceMap:e.writeSourceMap,rootNode:g,contentsMap:E.imports.contents,sourceMapFilename:e.sourceMapFilename,sourceMapURL:e.sourceMapURL,outputFilename:e.sourceMapOutputFilename,sourceMapBasepath:e.sourceMapBasepath,sourceMapRootpath:e.sourceMapRootpath,outputSourceFiles:e.outputSourceFiles,sourceMapGenerator:e.sourceMapGenerator})),h=g.toCSS({compress:Boolean(e.compress),dumpLineNumbers:a.dumpLineNumbers,strictUnits:Boolean(e.strictUnits),numPrecision:8})}catch(n){throw new u(n,a)}if(e.cleancss&&"node"===w.mode){var o=c("clean-css"),p=e.cleancssOptions||{};return p.keepSpecialComments===b&&(p.keepSpecialComments="*"),p.processImport=!1,p.noRebase=!0,p.noAdvanced===b&&(p.noAdvanced=!0),new o(p).minify(h)}return e.compress?h.replace(/(^(\s)+)|((\s)+$)/g,""):h}}(g.eval),y<v.length-1){y=A;var o=s(y,v);i=v.split("\n"),h=o.line+1,l={type:"Parse",message:"Unrecognised input",index:y,filename:a.currentFileInfo.filename,line:h,column:o.column,extract:[i[h-2],i[h-1],i[h]]}}var p=function(b){return b=l||b||E.imports.error,b?(b instanceof u||(b=new u(b,a)),e(b)):e(null,g)};return a.processImports===!1?p():void new x.importVisitor(this.imports,p).run(g)},parsers:F={primary:function(){for(var a,b=this.mixin,c=J,d=[];C;){if(a=this.extendRule()||b.definition()||this.rule()||this.ruleset()||b.call()||this.comment()||this.rulesetCall()||this.directive())d.push(a);else if(!c(/^[\s\n]+/)&&!c(/^;+/))break;if(q("}"))break}return d},comment:function(){var b;if("/"===v.charAt(y))return"/"===v.charAt(y+1)?new x.Comment(j(/^\/\/.*/),!0,y,a.currentFileInfo):(b=j(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/),b?new x.Comment(b,!1,y,a.currentFileInfo):void 0)},comments:function(){for(var a,b=[];;){if(a=this.comment(),!a)break;b.push(a)}return b},entities:{quoted:function(){var b,c,d=y,e=y;return"~"===v.charAt(d)&&(d++,c=!0),'"'===v.charAt(d)||"'"===v.charAt(d)?(c&&k("~"),b=j(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/),b?new x.Quoted(b[0],b[1]||b[2],c,e,a.currentFileInfo):void 0):void 0},keyword:function(){var a;if(a=j(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/)){var b=x.Color.fromKeyword(a);return b?b:new x.Keyword(a)}},call:function(){var b,c,d,e,f=y;if(b=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(C)){if(b=b[1],c=b.toLowerCase(),"url"===c)return null;if(y+=b.length,"alpha"===c&&(e=F.alpha(),"undefined"!=typeof e))return e;if(k("("),d=this.arguments(),k(")"))return b?new x.Call(b,d,f,a.currentFileInfo):void 0}},arguments:function(){for(var a,b=[];;){if(a=this.assignment()||F.expression(),!a)break;if(b.push(a),!k(","))break}return b},literal:function(){return this.dimension()||this.color()||this.quoted()||this.unicodeDescriptor()},assignment:function(){var a,b;return a=j(/^\w+(?=\s?=)/i),a&&k("=")?(b=F.entity(),b?new x.Assignment(a,b):void 0):void 0},url:function(){var b;if("u"===v.charAt(y)&&j(/^url\(/))return b=this.quoted()||this.variable()||j(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",n(")"),new x.URL(null!=b.value||b instanceof x.Variable?b:new x.Anonymous(b),a.currentFileInfo)},variable:function(){var b,c=y;return"@"===v.charAt(y)&&(b=j(/^@@?[\w-]+/))?new x.Variable(b,c,a.currentFileInfo):void 0},variableCurly:function(){var b,c=y;return"@"===v.charAt(y)&&(b=j(/^@\{([\w-]+)\}/))?new x.Variable("@"+b[1],c,a.currentFileInfo):void 0},color:function(){var a;return"#"===v.charAt(y)&&(a=j(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))?new x.Color(a[1]):void 0},dimension:function(){var a,b=v.charCodeAt(y);if(!(b>57||43>b||47===b||44==b))return a=j(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new x.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=j(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new x.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=y;return"~"===v.charAt(e)&&(e++,d=!0),"`"===v.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||o("You are using JavaScript, which has been disabled."),d&&k("~"),c=j(/^`([^`]*)`/),c?new x.JavaScript(c[1],y,d):void 0):void 0}},variable:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new x.RulesetCall(a[1]):void 0},extend:function(a){var b,c,d,e,f,g=y;if(j(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=null;!(d=j(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new x.Extend(new x.Selector(b),d,g),e?e.push(f):e=[f]}while(k(","));return m(/^\)/),a&&m(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,g,h,i,l,m=v.charAt(y),o=!1,p=y;if("."===m||"#"===m){for(d();;){if(b=y,h=j(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!h)break;g=new x.Element(i,h,b,a.currentFileInfo),c?c.push(g):c=[g],i=k(">")}return c&&(k("(")&&(l=this.args(!0).args,n(")")),F.important()&&(o=!0),F.end())?(f(),new x.mixin.Call(c,l,p,a.currentFileInfo,o)):void e()}},args:function(a){var b,c,g,h,i,l,m=E.parsers,n=m.entities,p={args:null,variadic:!1},q=[],r=[],s=[];for(d();;){if(a)l=m.detachedRuleset()||m.expression();else{if(m.comments(),"."===v.charAt(y)&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({variadic:!0});break}l=n.variable()||n.literal()||n.keyword()}if(!l)break;h=null,l.throwAwayComments&&l.throwAwayComments(),i=l;var t=null;if(a?l.value&&1==l.value.length&&(t=l.value[0]):t=l,t&&t instanceof x.Variable)if(k(":")){if(q.length>0&&(b&&o("Cannot mix ; and , as delimiter types"),c=!0),i=a&&m.detachedRuleset()||m.expression(),!i){if(!a)return e(),p.args=[],p;o("could not understand value for named argument")}h=g=t.name}else{if(!a&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({name:l.name,variadic:!0});break}a||(g=h=t.name,i=null)}i&&q.push(i),s.push({name:h,value:i}),k(",")||(k(";")||b)&&(c&&o("Cannot mix ; and , as delimiter types"),b=!0,q.length>1&&(i=new x.Value(q)),r.push({name:g,value:i}),g=null,q=[],c=!1)}return f(),p.args=b?r:s,p},definition:function(){var a,b,c,g,h=[],i=!1;if(!("."!==v.charAt(y)&&"#"!==v.charAt(y)||p(/^[^{]*\}/)))if(d(),b=j(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var l=this.args(!1);if(h=l.args,i=l.variadic,!k(")"))return A=y,void e();if(F.comments(),j(/^when/)&&(g=m(F.conditions,"expected condition")),c=F.block())return f(),new x.mixin.Definition(a,h,c,g,i);e()}else f()}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return k(";")||q("}")},alpha:function(){var a;if(j(/^\(opacity=/i))return a=j(/^\d+/)||this.entities.variable(),a?(n(")"),new x.Alpha(a)):void 0},element:function(){var b,c,g,h=y;return c=this.combinator(),b=j(/^(?:\d+\.\d+|\d+)%/)||j(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||k("*")||k("&")||this.attribute()||j(/^\([^()@]+\)/)||j(/^[\.#](?=@)/)||this.entities.variableCurly(),b||(d(),k("(")?(g=this.selector())&&k(")")?(b=new x.Paren(g),f()):e():f()),b?new x.Element(c,b,h,a.currentFileInfo):void 0},combinator:function(){var a=v.charAt(y);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(y++,"^"===v.charAt(y)&&(a="^^",y++);h(v,y);)y++;return new x.Combinator(a)}return new x.Combinator(h(v,y-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=y,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=m(this.conditions,"expected condition"):i?o("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&o("Extend can only be used at the end of selector"),e=v.charAt(y),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new x.Selector(c,d,i,j,a.currentFileInfo):void(d&&o("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(k("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=m(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=j(/^[|~*$^]?=/),c&&(b=d.quoted()||j(/^[0-9]+%/)||j(/^[\w-]+/)||d.variableCurly()),n("]"),new x.Attribute(a,c,b)}},block:function(){var a;return k("{")&&(a=this.primary())&&k("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new x.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new x.DetachedRuleset(a):void 0},ruleset:function(){var b,c,g,h;for(d(),a.dumpLineNumbers&&(h=t(y,v,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&o("Guards are only currently allowed on a single selector."),!k(","))break;c.condition&&o("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(g=this.block())){f();var i=new x.Ruleset(b,g,a.strictImports);return a.dumpLineNumbers&&(i.debugInfo=h),i}A=y,e()},rule:function(b){var c,g,h,i,j,k=y,l=v.charAt(k);if("."!==l&&"#"!==l&&"&"!==l)if(d(),c=this.variable()||this.ruleProperty()){if(j="string"==typeof c,j&&(g=this.detachedRuleset()),g||(g=b||!a.compress&&!j?this.anonymousValue()||this.value():this.value()||this.anonymousValue(),h=this.important(),i=!j&&c.pop().value),g&&this.end())return f(),new x.Rule(c,g,h,i,k,a.currentFileInfo);if(A=y,e(),g&&!b)return this.rule(!0)}else f()},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(C),a?(y+=a[0].length-1,new x.Anonymous(a[1])):void 0},"import":function(){var b,c,g=y;d();var h=j(/^@import?\s+/),i=(h?this.importOptions():null)||{};return h&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),k(";"))?(f(),c=c&&new x.Value(c),new x.Import(b,c,i,g,a.currentFileInfo)):void e()},importOptions:function(){var a,b,c,d={};if(!k("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!k(","))break}while(a);return n(")"),d},importOption:function(){var a=j(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(k("(")){if(c=this.property(),b=this.value(),!k(")"))return null;if(c&&b)e.push(new x.Paren(new x.Rule(c,b,null,null,y,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new x.Paren(b))}}while(b);return e.length>0?new x.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!k(","))break}else if(a=b.variable(),a&&(c.push(a),!k(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=t(y,v,a)),j(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new x.Media(c,b,y,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,g,h,i,l,m,n=y,p=!0;if("@"===v.charAt(y)){if(c=this["import"]()||this.media())return c;if(d(),b=j(/^@[a-z-]+/)){switch(h=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(h="@"+b.slice(b.indexOf("-",2)+1)),h){case"@charset":i=!0,p=!1;break;case"@namespace":l=!0,p=!1;break;case"@keyframes":i=!0;break;case"@host":case"@page":case"@document":case"@supports":m=!0}return i?(c=this.entity(),c||o("expected "+b+" identifier")):l?(c=this.expression(),c||o("expected "+b+" expression")):m&&(c=(j(/^[^{;]+/)||"").trim(),c&&(c=new x.Anonymous(c))),p&&(g=this.blockRuleset()),g||!p&&c&&k(";")?(f(),new x.Directive(b,c,g,n,a.currentFileInfo,a.dumpLineNumbers?t(n,v,a):null)):void e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!k(",")))break;while(a);return b.length>0?new x.Value(b):void 0},important:function(){return"!"===v.charAt(y)?j(/^! *important/):void 0},sub:function(){var a,b;return k("(")&&(a=this.addition())?(b=new x.Expression([a]),n(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=this.operand()){for(e=h(v,y-1);;){if(p(/^\/[*\/]/))break;if(c=k("/")||k("*"),!c)break;if(b=this.operand(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=h(v,y-1);;){if(c=j(/^[-+]\s+/)||!e&&(k("+")||k("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},conditions:function(){var a,b,c,d=y;if(a=this.condition()){for(;;){if(!p(/^,\s*(not\s*)?\(/)||!k(","))break;if(b=this.condition(),!b)break;c=new x.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=y,g=!1;return j(/^not/)&&(g=!0),n("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=j(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new x.Condition(d,a,b,f,g):o("expected expression")):c=new x.Condition("=",a,new x.Keyword("true"),f,g),n(")"),j(/^and/)?new x.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=v.charAt(y+1);"-"!==v.charAt(y)||"@"!==c&&"("!==c||(a=k("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new x.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),p(/^\/[\/*]/)||(b=k("/"),b&&c.push(new x.Anonymous(b))));while(a);return c.length>0?new x.Expression(c):void 0},property:function(){var a=j(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(e);return b?(g.push(y+h),h+=b[0].length,e=e.slice(b[1].length),f.push(b[1])):void 0}var c,d,e=C,f=[],g=[],h=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(f.length>1&&b(/^\s*((?:\+_|\+)?)\s*:/)){for(l(h),""===f[0]&&(f.shift(),g.shift()),d=0;d<f.length;d++)c=f[d],f[d]="@"!==c.charAt(0)?new x.Keyword(c):new x.Variable("@"+c.slice(2,-1),g[d],a.currentFileInfo);return f}}}}},w.Parser.serializeVars=function(a){var b="";for(var c in a)if(Object.hasOwnProperty.call(a,c)){var d=a[c];b+=("@"===c[0]?"":"@")+c+": "+d+(";"===(""+d).slice(-1)?"":";")}return b},function(d){function e(a,b,c){if(!(c instanceof d.Dimension))throw{type:"Argument",message:"argument must be a number"};return null==b?b=c.unit:c=c.unify(),new d.Dimension(a(parseFloat(c.value)),b)}function f(a,b,c){var e,f,g,h,i=b.alpha,j=c.alpha,k=[];g=j+i*(1-j);for(var l=0;3>l;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new d.Dimension(Math.round(b*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()<d?c:b},e:function(a){return new d.Anonymous(a instanceof d.JavaScript?a.evaluated:a)},escape:function(a){return new d.Anonymous(encodeURI(a.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},replace:function(a,b,c,e){var f=a.value;return f=f.replace(new RegExp(b.value,e?e.value:""),c.value),new d.Quoted(a.quote||"",f,a.escaped)},"%":function(a){for(var b=Array.prototype.slice.call(arguments,1),c=a.value,e=0;e<b.length;e++)c=c.replace(/%[sda]/i,function(a){var c=a.match(/s/i)?b[e].value:b[e].toCSS();return a.match(/[A-Z]$/)?encodeURIComponent(c):c});return c=c.replace(/%%/g,"%"),new d.Quoted(a.quote||"",c,a.escaped)
+},unit:function(a,b){if(!(a instanceof d.Dimension))throw{type:"Argument",message:"the first argument to unit must be a number"+(a instanceof d.Operation?". Have you forgotten parenthesis?":"")};return b=b?b instanceof d.Keyword?b.value:b.toCSS():"",new d.Dimension(a.value,b)},convert:function(a,b){return a.convertTo(b.value)},round:function(a,b){var c="undefined"==typeof b?0:b.value;return e(function(a){return a.toFixed(c)},null,a)},pi:function(){return new d.Dimension(Math.PI)},mod:function(a,b){return new d.Dimension(a.value%b.value,a.unit)},pow:function(a,b){if("number"==typeof a&&"number"==typeof b)a=new d.Dimension(a),b=new d.Dimension(b);else if(!(a instanceof d.Dimension&&b instanceof d.Dimension))throw{type:"Argument",message:"arguments must be numbers"};return new d.Dimension(Math.pow(a.value,b.value),a.unit)},_minmax:function(a,c){switch(c=Array.prototype.slice.call(c),c.length){case 0:throw{type:"Argument",message:"one or more arguments required"}}var e,f,g,h,i,j,k,l,m=[],n={};for(e=0;e<c.length;e++)if(g=c[e],g instanceof d.Dimension)if(h=""===g.unit.toString()&&l!==b?new d.Dimension(g.value,l).unify():g.unify(),j=""===h.unit.toString()&&k!==b?k:h.unit.toString(),k=""!==j&&k===b||""!==j&&""===m[0].unify().unit.toString()?j:k,l=""!==j&&l===b?g.unit.toString():l,f=n[""]!==b&&""!==j&&j===k?n[""]:n[j],f!==b)i=""===m[f].unit.toString()&&l!==b?new d.Dimension(m[f].value,l).unify():m[f].unify(),(a&&h.value<i.value||!a&&h.value>i.value)&&(m[f]=g);else{if(k!==b&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(c[e].value)&&Array.prototype.push.apply(c,Array.prototype.slice.call(c[e].value));return 1==m.length?m[0]:(c=m.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},"get-unit":function(a){return new d.Anonymous(a.unit)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<?xml version="1.0" ?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none"><'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;h<m.length;h+=1)m[h].value?(i=m[h].value[0],j=m[h].value[1]):(i=m[h],j=b),i instanceof d.Color&&((0===h||h+1===m.length)&&j===b||j instanceof d.Dimension)||e(),k=j?j.toCSS(q):0===h?"0%":"100%",l=i.alpha,g+='<stop offset="'+k+'" stop-color="'+i.toRGB()+'"'+(1>l?' stop-opacity="'+l+'"':"")+"/>";if(g+="</"+n+"Gradient><rect "+o+' fill="url(#gradient)" /></svg>',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c;return a&&null!=a.numPrecision?(c=Math.pow(10,a.numPrecision),Math.round(b*c)/c):b},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d<a.length;d++)if(c=b.call(a,a[d]))return c;return null},a.jsify=function(a){return Array.isArray(a.value)&&a.value.length>1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a.value||a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;c<this.args.length;c++)this.args[c].genCSS(a,b),c+1<this.args.length&&b.add(", ");b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){function b(a){return"#"+a.map(function(a){return a=c(Math.round(a),255),(16>a?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255;return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.DetachedRuleset=function(a,b){this.ruleset=a,this.frames=b},a.DetachedRuleset.prototype={type:"DetachedRuleset",accept:function(a){this.ruleset=a.visit(this.ruleset)},eval:function(b){var c=this.frames||b.frames.slice(0);return new a.DetachedRuleset(this.ruleset,c)},callEval:function(b){return this.ruleset.eval(this.frames?new a.evalEnv(b,this.frames.concat(b.frames)):b)}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return void c.add(e);d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c,d,e,f;if(this.unit.isEmpty()||b.unit.isEmpty())c=this,d=b;else if(c=this.unify(),d=b.unify(),0!==c.unit.compare(d.unit))return-1;return e=c.value,f=d.value,f>e?-1:e>f?1:0}return-1},unify:function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,px:.0254/96,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a<this.denominator.length;a++)b+="/"+this.denominator[a];return b},compare:function(a){return this.is(a.toString())?0:-1},is:function(a){return this.toString()===a},isLength:function(){return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/))},isEmpty:function(){return 0===this.numerator.length&&0===this.denominator.length},isSingular:function(){return this.numerator.length<=1&&0===this.denominator.length},map:function(a){var b;for(b=0;b<this.numerator.length;b++)this.numerator[b]=a(this.numerator[b],!1);for(b=0;b<this.denominator.length;b++)this.denominator[b]=a(this.denominator[b],!0)},usedUnits:function(){var b,c,d={};c=function(a){return b.hasOwnProperty(a)&&!d[e]&&(d[e]=a),a};for(var e in a.UnitConversions)a.UnitConversions.hasOwnProperty(e)&&(b=a.UnitConversions[e],this.map(c));return d},cancel:function(){var a,b,c,d={};for(b=0;b<this.numerator.length;b++)a=this.numerator[b],c||(c=a),d[a]=(d[a]||0)+1;for(b=0;b<this.denominator.length;b++)a=this.denominator[b],c||(c=a),d[a]=(d[a]||0)-1;this.numerator=[],this.denominator=[];for(a in d)if(d.hasOwnProperty(a)){var e=d[a];if(e>0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(a,b,c,d,e,f){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f},a.Directive.prototype={type:"Directive",accept:function(a){var b=this.value,c=this.rules;c&&(c=a.visit(c)),b&&(b=a.visit(b))},genCSS:function(b,c){var d=this.value,e=this.rules;c.add(this.name,this.currentFileInfo,this.index),d&&(c.add(" "),d.genCSS(b,c)),e?a.outputRuleset(b,c,[e]):c.add(";")},toCSS:a.toCSS,eval:function(b){var c=this.value,d=this.rules;return c&&(c=c.eval(b)),d&&(d=d.eval(b),d.root=!0),new a.Directive(this.name,c,d,this.index,this.currentFileInfo,this.debugInfo)},variable:function(b){return this.rules?a.Ruleset.prototype.variable.call(this.rules,b):void 0},find:function(){return this.rules?a.Ruleset.prototype.find.apply(this.rules,arguments):void 0},rulesets:function(){return this.rules?a.Ruleset.prototype.rulesets.apply(this.rules):void 0},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;a<b.length;a++)b[a].markReferenced&&b[a].markReferenced()}}}(c("../tree")),function(a){a.Element=function(b,c,d,e){this.combinator=b instanceof a.Combinator?b:new a.Combinator(b),this.value="string"==typeof c?c.trim():c?c:"",this.index=d,this.currentFileInfo=e},a.Element.prototype={type:"Element",accept:function(a){var b=this.value;this.combinator=a.visit(this.combinator),"object"==typeof b&&(this.value=a.visit(b))},eval:function(b){return new a.Element(this.combinator,this.value.eval?this.value.eval(b):this.value,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.toCSS(a),this.currentFileInfo,this.index)},toCSS:function(a){var b=this.value.toCSS?this.value.toCSS(a):this.value;return""===b&&"&"===this.combinator.value.charAt(0)?"":this.combinator.toCSS(a||{})+b}},a.Attribute=function(a,b,c){this.key=a,this.op=b,this.value=c},a.Attribute.prototype={type:"Attribute",eval:function(b){return new a.Attribute(this.key.eval?this.key.eval(b):this.key,this.op,this.value&&this.value.eval?this.value.eval(b):this.value)},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(a){var b=this.key.toCSS?this.key.toCSS(a):this.key;return this.op&&(b+=this.op,b+=this.value.toCSS?this.value.toCSS(a):this.value),"["+b+"]"}},a.Combinator=function(a){this.value=" "===a?" ":a?a.trim():""},a.Combinator.prototype={type:"Combinator",_outputMap:{"":""," ":" ",":":" :","+":" + ","~":" ~ ",">":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c<this.value.length;c++)this.value[c].genCSS(a,b),c+1<this.value.length&&b.add(" ")},toCSS:a.toCSS,throwAwayComments:function(){this.value=this.value.filter(function(b){return!(b instanceof a.Comment)})}}}(c("../tree")),function(a){a.Extend=function(b,c,d){switch(this.selector=b,this.option=c,this.index=d,this.object_id=a.Extend.next_id++,this.parent_ids=[this.object_id],c){case"all":this.allowBefore=!0,this.allowAfter=!0;break;default:this.allowBefore=!1,this.allowAfter=!1}},a.Extend.next_id=0,a.Extend.prototype={type:"Extend",accept:function(a){this.selector=a.visit(this.selector)},eval:function(b){return new a.Extend(this.selector.eval(b),this.option,this.index)},clone:function(){return new a.Extend(this.selector,this.option,this.index)},findSelfSelectors:function(a){var b,c,d=[];for(b=0;b<a.length;b++)c=a[b].elements,b>0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):new a.Anonymous(Array.isArray(c)?c.join(", "):c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;a<b.length;a++)b[a].markReferenced&&b[a].markReferenced()},evalTop:function(b){var c=this;if(b.mediaBlocks.length>1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c<e.length;c++)d=e[c].features instanceof a.Value?e[c].features.value:e[c].features,e[c]=Array.isArray(d)?d:[d];return this.features=new a.Value(this.permute(e).map(function(b){for(b=b.map(function(b){return b.toCSS?b:new a.Anonymous(b)}),c=b.length-1;c>0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d<c.length;d++)for(var e=0;e<a[0].length;e++)b.push([a[0][e]].concat(c[d]));return b},bubbleSelectors:function(b){b&&(this.rules=[new a.Ruleset(b.slice(0),[this.rules[0]])])}}}(c("../tree")),function(a){a.mixin={},a.mixin.Call=function(b,c,d,e,f){this.selector=new a.Selector(b),this.arguments=c&&c.length?c:null,this.index=d,this.currentFileInfo=e,this.important=f},a.mixin.Call.prototype={type:"MixinCall",accept:function(a){this.selector&&(this.selector=a.visit(this.selector)),this.arguments&&(this.arguments=a.visitArray(this.arguments))
+},eval:function(b){var c,d,e,f,g,h,i,j,k,l,m,n,o=[],p=!1,q=[],r=[],s=a.defaultFunc,t=0,u=1,v=2;for(e=this.arguments&&this.arguments.map(function(a){return{name:a.name,value:a.value.eval(b)}}),f=0;f<b.frames.length;f++)if((c=b.frames[f].find(this.selector)).length>0){for(j=!0,g=0;g<c.length;g++){for(d=c[g],i=!1,h=0;h<b.frames.length;h++)if(!(d instanceof a.mixin.Definition)&&d===(b.frames[h].originalRuleset||b.frames[h])){i=!0;break}if(!i&&d.matchArgs(e,b)){if(l={mixin:d,group:t},d.matchCondition){for(h=0;2>h;h++)s.value(h),r[h]=d.matchCondition(e,b);(r[0]||r[1])&&(r[0]!=r[1]&&(l.group=r[1]?u:v),q.push(l))}else q.push(l);p=!0}}for(s.reset(),n=[0,0,0],g=0;g<q.length;g++)n[q[g].group]++;if(n[t]>0)m=v;else if(m=u,n[u]+n[v]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;g<q.length;g++)if(l=q[g].group,l===t||l===m)try{d=q[g].mixin,d instanceof a.mixin.Definition||(d=new a.mixin.Definition("",[],d.rules,null,!1),d.originalRuleset=c[g].originalRuleset||c[g]),Array.prototype.push.apply(o,d.evalCall(b,e,this.important).rules)}catch(w){throw{message:w.message,index:this.index,filename:this.currentFileInfo.filename,stack:w.stack}}if(p){if(!this.currentFileInfo||!this.currentFileInfo.reference)for(f=0;f<o.length;f++)k=o[f],k.markReferenced&&k.markReferenced();return o}}throw j?{type:"Runtime",message:"No matching definition was found for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename}:{type:"Name",message:this.selector.toCSS().trim()+" is undefined",index:this.index,filename:this.currentFileInfo.filename}},format:function(a){return this.selector.toCSS().trim()+"("+(a?a.map(function(a){var b="";return a.name&&(b+=a.name+":"),b+=a.value.toCSS?a.value.toCSS():"???"}).join(", "):"")+")"}},a.mixin.Definition=function(b,c,d,e,f,g){this.name=b,this.selectors=[new a.Selector([new a.Element(null,b,this.index,this.currentFileInfo)])],this.params=c,this.condition=e,this.variadic=f,this.arity=c.length,this.rules=d,this._lookups={},this.required=c.reduce(function(a,b){return!b.name||b.name&&!b.value?a+1:a},0),this.parent=a.Ruleset.prototype,this.frames=g},a.mixin.Definition.prototype={type:"MixinDefinition",accept:function(a){this.params&&this.params.length&&(this.params=a.visitArray(this.params)),this.rules=a.visitArray(this.rules),this.condition&&(this.condition=a.visit(this.condition))},variable:function(a){return this.parent.variable.call(this,a)},variables:function(){return this.parent.variables.call(this)},find:function(){return this.parent.find.apply(this,arguments)},rulesets:function(){return this.parent.rulesets.apply(this)},evalParams:function(b,c,d,e){var f,g,h,i,j,k,l,m,n=new a.Ruleset(null,null),o=this.params.slice(0),p=0;if(c=new a.evalEnv(c,[n].concat(c.frames)),d)for(d=d.slice(0),p=d.length,h=0;p>h;h++)if(g=d[h],k=g&&g.name){for(l=!1,i=0;i<o.length;i++)if(!e[i]&&k===o[i].name){e[i]=g.value.eval(b),n.prependRule(new a.Rule(k,g.value.eval(b))),l=!0;break}if(l){d.splice(h,1),h--;continue}throw{type:"Runtime",message:"Named argument for "+this.name+" "+d[h].name+" not found"}}for(m=0,h=0;h<o.length;h++)if(!e[h]){if(g=d&&d[m],k=o[h].name)if(o[h].variadic){for(f=[],i=m;p>i;i++)f.push(d[i].value.eval(b));n.prependRule(new a.Rule(k,new a.Expression(f).eval(b)))}else{if(j=g&&g.value)j=j.eval(b);else{if(!o[h].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+p+" for "+this.arity+")"};j=o[h].value.eval(c),n.resetCache()}n.prependRule(new a.Rule(k,j)),e[h]=j}if(o[h].variadic&&d)for(i=m;p>i;i++)e[i]=d[i].value.eval(b);m++}return n},eval:function(b){return new a.mixin.Definition(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||b.frames.slice(0))},evalCall:function(b,c,d){var e,f,g=[],h=this.frames?this.frames.concat(b.frames):b.frames,i=this.evalParams(b,new a.evalEnv(b,h),c,g);return i.prependRule(new a.Rule("@arguments",new a.Expression(g).eval(b))),e=this.rules.slice(0),f=new a.Ruleset(null,e),f.originalRuleset=this,f=f.eval(new a.evalEnv(b,[this,i].concat(h))),d&&(f=this.parent.makeImportant.apply(f)),f},matchCondition:function(b,c){return this.condition&&!this.condition.eval(new a.evalEnv(c,[this.evalParams(c,new a.evalEnv(c,this.frames.concat(c.frames)),b,[])].concat(this.frames).concat(c.frames)))?!1:!0},matchArgs:function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(d<this.required-1)return!1}else{if(d<this.required)return!1;if(d>this.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function b(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value||c instanceof a.Ruleset?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d,e=!1,f=this.name;"string"!=typeof f&&(f=1===f.length&&f[0]instanceof a.Keyword?f[0].value:b(c,f)),"font"!==f||c.strictMath||(e=!0,c.strictMath=!0);try{if(d=this.value.eval(c),!this.variable&&"DetachedRuleset"===d.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};return new a.Rule(f,d,this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(g){throw"number"!=typeof g.index&&(g.index=this.index,g.filename=this.currentFileInfo.filename),g}finally{e&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.RulesetCall=function(a){this.variable=a},a.RulesetCall.prototype={type:"RulesetCall",accept:function(){},eval:function(b){var c=new a.Variable(this.variable).eval(b);return c.callEval(b)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f,g=this.selectors,h=a.defaultFunc,i=!1;if(g&&(d=g.length)){for(c=[],h.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;d>f;f++)e=g[f].eval(b),c.push(e),e.evaldCondition&&(i=!0);h.reset()}else i=!0;var j,k,l=this.rules?this.rules.slice(0):null,m=new a.Ruleset(c,l,this.strictImports);m.originalRuleset=this,m.root=this.root,m.firstRoot=this.firstRoot,m.allowImports=this.allowImports,this.debugInfo&&(m.debugInfo=this.debugInfo),i||(l.length=0);var n=b.frames;n.unshift(m);var o=b.selectors;o||(b.selectors=o=[]),o.unshift(this.selectors),(m.root||m.allowImports||!m.strictImports)&&m.evalImports(b);var p=m.rules,q=p?p.length:0;for(f=0;q>f;f++)(p[f]instanceof a.mixin.Definition||p[f]instanceof a.DetachedRuleset)&&(p[f]=p[f].eval(b));var r=b.mediaBlocks&&b.mediaBlocks.length||0;for(f=0;q>f;f++)p[f]instanceof a.mixin.Call?(l=p[f].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!m.variable(b.name):!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache()):p[f]instanceof a.RulesetCall&&(l=p[f].eval(b).rules.filter(function(b){return b instanceof a.Rule&&b.variable?!1:!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache());for(f=0;f<p.length;f++)j=p[f],j instanceof a.mixin.Definition||j instanceof a.DetachedRuleset||(p[f]=j=j.eval?j.eval(b):j);for(f=0;f<p.length;f++)if(j=p[f],j instanceof a.Ruleset&&j.selectors&&1===j.selectors.length&&j.selectors[0].isJustParentSelector()){p.splice(f--,1);for(var s=0;s<j.rules.length;s++)k=j.rules[s],k instanceof a.Rule&&k.variable||p.splice(++f,0,k)}if(n.shift(),o.shift(),b.mediaBlocks)for(f=r;f<b.mediaBlocks.length;f++)b.mediaBlocks[f].bubbleSelectors(c);return m},evalImports:function(b){var c,d,e=this.rules;if(e)for(c=0;c<e.length;c++)e[c]instanceof a.Import&&(d=e[c].eval(b),d&&d.length?(e.splice.apply(e,[c,1].concat(d)),c+=d.length-1):e.splice(c,1,d),this.resetCache())},makeImportant:function(){return new a.Ruleset(this.selectors,this.rules.map(function(a){return a.makeImportant?a.makeImportant():a}),this.strictImports)},matchArgs:function(a){return!a||0===a.length},matchCondition:function(b,c){var d=this.selectors[this.selectors.length-1];return d.evaldCondition?d.condition&&!d.condition.eval(new a.evalEnv(c,c.frames))?!1:!0:!1},resetCache:function(){this._rulesets=null,this._variables=null,this._lookups={}},variables:function(){return this._variables||(this._variables=this.rules?this.rules.reduce(function(b,c){return c instanceof a.Rule&&c.variable===!0&&(b[c.name]=c),b},{}):{}),this._variables},variable:function(a){return this.variables()[a]},rulesets:function(){if(!this.rules)return null;var b,c,d=a.Ruleset,e=a.mixin.Definition,f=[],g=this.rules,h=g.length;for(b=0;h>b;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;g<f.selectors.length;g++)if(d=b.match(f.selectors[g])){b.elements.length>d?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;d<this.rules.length;d++)h=this.rules[d],h.rules||h instanceof a.Media||h instanceof a.Directive||this.root&&h instanceof a.Comment?k.push(h):j.push(h);if(!this.root){g=a.debugInfo(b,this,n),g&&(c.add(g),c.add(n));var o,p=this.paths,q=p.length;for(l=b.compress?",":",\n"+n,d=0;q>d;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;d<j.length;d++)h=j[d],d+1!==j.length||this.root&&0!==k.length&&!this.firstRoot||(b.lastRule=!0),h.genCSS?h.genCSS(b,c):h.value&&c.add(h.value.toString()),b.lastRule?b.lastRule=!1:c.add(b.compress?"":"\n"+m);if(this.root||(c.add(b.compress?"}":"\n"+n+"}"),b.tabLevel--),l=(b.compress?"":"\n")+(this.root?m:n),f=k.length)for(j.length&&l&&c.add(l),k[0].genCSS(b,c),d=1;f>d;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){if(this.selectors)for(var a=0;a<this.selectors.length;a++)this.selectors[a].markReferenced()},joinSelectors:function(a,b,c){for(var d=0;d<c.length;d++)this.joinSelector(a,b,c[d])},joinSelector:function(b,c,d){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;for(e=0;e<d.elements.length;e++)j=d.elements[e],"&"===j.value&&(h=!0);if(h){for(r=[],i=[[]],e=0;e<d.elements.length;e++)if(j=d.elements[e],"&"!==j.value)r.push(j);else{for(s=[],r.length>0&&this.mergeElementsOnToSelectors(r,i),f=0;f<i.length;f++)if(k=i[f],0===c.length)k.length>0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g<c.length;g++)l=c[g],m=[],n=[],p=!0,k.length>0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e<i.length;e++)i[e].length>0&&b.push(i[e])}else if(c.length>0)for(e=0;e<c.length;e++)b.push(c[e].concat(d));else b.push([d])},mergeElementsOnToSelectors:function(b,c){var d,e;if(0===c.length)return void c.push([new a.Selector(b)]);for(d=0;d<c.length;d++)e=c[d],e.length>0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;c<this.elements.length;c++)d=this.elements[c],d.genCSS(a,b)},toCSS:a.toCSS,markReferenced:function(){this.isReferenced=!0},getIsReferenced:function(){return!this.currentFileInfo.reference||this.isReferenced},getIsOutput:function(){return this.evaldCondition}}}(c("../tree")),function(a){a.UnicodeDescriptor=function(a){this.value=a},a.UnicodeDescriptor.prototype={type:"UnicodeDescriptor",genCSS:function(a,b){b.add(this.value)},toCSS:a.toCSS,eval:function(){return this}}}(c("../tree")),function(a){a.URL=function(a,b,c){this.value=a,this.currentFileInfo=b,this.isEvald=c},a.URL.prototype={type:"Url",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("url("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){var c,d=this.value.eval(b);if(!this.isEvald&&(c=this.currentFileInfo&&this.currentFileInfo.rootpath,c&&"string"==typeof d.value&&b.isPathRelative(d.value)&&(d.quote||(c=c.replace(/[\(\)'"\s]/g,function(a){return"\\"+a})),d.value=c+d.value),d.value=b.normalizePath(d.value),b.urlArgs&&!d.value.match(/^\s*data:/))){var e=-1===d.value.indexOf("?")?"?":"&",f=e+b.urlArgs;-1!==d.value.indexOf("#")?d.value=d.value.replace("#",f+"#"):d.value+=f}return new a.URL(d,this.currentFileInfo,!0)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a},a.Value.prototype={type:"Value",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){return 1===this.value.length?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},genCSS:function(a,b){var c;for(c=0;c<this.value.length;c++)this.value[c].genCSS(a,b),c+1<this.value.length&&b.add(a&&a.compress?",":", ")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Variable=function(a,b,c){this.name=a,this.index=b,this.currentFileInfo=c||{}},a.Variable.prototype={type:"Variable",eval:function(b){var c,d=this.name;if(0===d.indexOf("@@")&&(d="@"+new a.Variable(d.slice(1)).eval(b).value),this.evaluating)throw{type:"Name",message:"Recursive variable definition for "+d,filename:this.currentFileInfo.file,index:this.index};if(this.evaluating=!0,c=a.find(b.frames,function(a){var c=a.variable(d);return c?c.value.eval(b):void 0}))return this.evaluating=!1,c;throw{type:"Name",message:"variable "+d+" is undefined",filename:this.currentFileInfo.filename,index:this.index}}}}(c("../tree")),function(a){var b=["paths","optimization","files","contents","contentsIgnoredChars","relativeUrls","rootpath","strictImports","insecure","dumpLineNumbers","compress","processImports","syncImport","javascriptEnabled","mime","useFileCache","currentFileInfo"];a.parseEnv=function(a){if(d(a,this,b),this.contents||(this.contents={}),this.contentsIgnoredChars||(this.contentsIgnoredChars={}),this.files||(this.files={}),!this.currentFileInfo){var c=a&&a.filename||"input",e=c.replace(/[^\/\\]*$/,"");a&&(a.filename=null),this.currentFileInfo={filename:c,relativeUrls:this.relativeUrls,rootpath:a&&a.rootpath||"",currentDirectory:e,entryPath:e,rootFilename:c}}};var c=["silent","verbose","compress","yuicompress","ieCompat","strictMath","strictUnits","cleancss","sourceMap","importMultiple","urlArgs"];a.evalEnv=function(a,b){d(a,this,c),this.frames=b||[]},a.evalEnv.prototype.inParenthesis=function(){this.parensStack||(this.parensStack=[]),this.parensStack.push(!0)},a.evalEnv.prototype.outOfParenthesis=function(){this.parensStack.pop()},a.evalEnv.prototype.isMathOn=function(){return this.strictMath?this.parensStack&&this.parensStack.length:!0},a.evalEnv.prototype.isPathRelative=function(a){return!/^(?:[a-z-]+:|\/)/.test(a)},a.evalEnv.prototype.normalizePath=function(a){var b,c=a.split("/").reverse();for(a=[];0!==c.length;)switch(b=c.pop()){case".":break;case"..":0===a.length||".."===a[a.length-1]?a.push(b):a.pop();break;default:a.push(b)}return a.join("/")};var d=function(a,b,c){if(a)for(var d=0;d<c.length;d++)a.hasOwnProperty(c[d])&&(b[c[d]]=a[c[d]])}}(c("./tree")),function(a){function b(a){return a}function c(a,b){var d,e;for(d in a)if(a.hasOwnProperty(d))switch(e=a[d],typeof e){case"function":e.prototype&&e.prototype.type&&(e.prototype.typeIndex=b++);break;case"object":b=c(e,b)}return b}var d={visitDeeper:!0},e=!1;a.visitor=function(b){this._implementation=b,this._visitFnCache=[],e||(c(a,1),e=!0)},a.visitor.prototype={visit:function(a){if(!a)return a;var c=a.typeIndex;if(!c)return a;var e,f=this._visitFnCache,g=this._implementation,h=c<<1,i=1|h,j=f[h],k=f[i],l=d;if(l.visitDeeper=!0,j||(e="visit"+a.type,j=g[e]||b,k=g[e+"Out"]||b,f[h]=j,f[i]=k),j!==b){var m=j.call(g,a,l);g.isReplacing&&(a=m)}return l.visitDeeper&&a&&a.accept&&a.accept(this),k!=b&&k.call(g,a),a},visitArray:function(a,b){if(!a)return a;var c,d=a.length;if(b||!this._implementation.isReplacing){for(c=0;d>c;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d,e,f){if(this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0,this.onceFileDetectionMap=e||{},this.recursionDetector={},f)for(var g in f)f.hasOwnProperty(g)&&(this.recursionDetector[g]=!0)},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),h.importMultiple||(b.skip=g?!0:function(){return i in e.onceFileDetectionMap?!0:(e.onceFileDetectionMap[i]=!0,!1)});var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};if(d){b.root=d,b.importedFilename=i;var k=g||i in e.recursionDetector;if(!f&&(h.importMultiple||!k))return e.recursionDetector[i]=!0,void new a.importVisitor(e._importer,j,h,e.onceFileDetectionMap,e.recursionDetector).run(d)}j()})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d<b.length;d++)if(c=b[d],c instanceof a.Rule&&!c.variable)throw{message:"properties must be inside selector blocks, they cannot be in the root.",index:c.index,filename:c.currentFileInfo?c.currentFileInfo.filename:null}},visitRuleset:function(b,c){var d,e=[];if(b.firstRoot&&this.checkPropertiesInRoot(b.rules),b.root)b.accept(this._visitor),c.visitDeeper=!1,(b.firstRoot||b.rules&&b.rules.length>0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;c<b.length;c++)if(b[c].getIsReferenced()&&b[c].getIsOutput())return!0;return!1}));for(var f=b.rules,g=f?f.length:0,h=0;g>h;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g<b.length;g++)d=b[g],d instanceof a.Rule&&d.merge&&(e=[d.name,d.important?"!":""].join(","),f[e]?b.splice(g--,1):f[e]=[],f[e].push(d));Object.keys(f).map(function(b){function e(b){return new a.Expression(b.map(function(a){return a.value}))}function g(b){return new a.Value(b.map(function(a){return a}))}if(c=f[b],c.length>1){d=c[0];var h=[],i=[];c.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c<j.length;c++){var k=j[c],l=k[k.length-1],m=l.extendList;for(f=m?m.slice(0).concat(g):g,f&&(f=f.map(function(a){return a.clone()})),d=0;d<f.length;d++)this.foundExtends=!0,e=f[d],e.findSelfSelectors(k),e.ruleset=b,0===d&&(e.firstExtendOnThisSelectorPath=!0),this.allExtendsStack[this.allExtendsStack.length-1].push(e)}this.contexts.push(b.selectors)}},visitRulesetOut:function(a){a.root||(this.contexts.length=this.contexts.length-1)},visitMedia:function(a){a.allExtends=[],this.allExtendsStack.push(a.allExtends)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){a.allExtends=[],this.allExtendsStack.push(a.allExtends)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}},a.processExtendsVisitor=function(){this._visitor=new a.visitor(this)},a.processExtendsVisitor.prototype={run:function(b){var c=new a.extendFinderVisitor;return c.run(b),c.foundExtends?(b.allExtends=b.allExtends.concat(this.doExtendChaining(b.allExtends,b.allExtends)),this.allExtendsStack=[b.allExtends],this._visitor.visit(b)):b},doExtendChaining:function(b,c,d){var e,f,g,h,i,j,k,l,m=[],n=this;for(d=d||0,e=0;e<b.length;e++)for(f=0;f<c.length;f++)j=b[e],k=c[f],j.parent_ids.indexOf(k.object_id)>=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d<f.length;d++)for(c=0;c<a.paths.length;c++)if(e=a.paths[c],!a.extendOnEveryPath){var i=e[e.length-1].extendList;i&&i.length||(b=this.findMatch(f[d],e),b.length&&f[d].selfSelectors.forEach(function(a){g.push(h.extendSelector(b,e,a))}))}a.paths=a.paths.concat(g)}},findMatch:function(a,b){var c,d,e,f,g,h,i,j=this,k=a.selector.elements,l=[],m=[];for(c=0;c<b.length;c++)for(d=b[c],e=0;e<d.elements.length;e++)for(f=d.elements[e],(a.allowBefore||0===c&&0===e)&&l.push({pathIndex:c,index:e,matched:0,initialCombinator:f.combinator}),h=0;h<l.length;h++)i=l[h],g=f.combinator.value,""===g&&0===e&&(g=" "),!j.isElementValuesEqual(k[i.matched].value,f.value)||i.matched>0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1<d.elements.length||c+1<b.length)&&(i=null)),i?i.finished&&(i.length=k.length,i.endPathIndex=c,i.endPathElementIndex=e+1,l.length=0,m.push(i)):(l.splice(h,1),h--);return m},isElementValuesEqual:function(b,c){if("string"==typeof b||"string"==typeof c)return b===c;if(b instanceof a.Attribute)return b.op!==c.op||b.key!==c.key?!1:b.value&&c.value?(b=b.value.value||b.value,c=c.value.value||c.value,b===c):b.value||c.value?!1:!0;if(b=b.value,c=c.value,b instanceof a.Selector){if(!(c instanceof a.Selector)||b.elements.length!==c.elements.length)return!1;for(var d=0;d<b.elements.length;d++){if(b.elements[d].combinator.value!==c.elements[d].combinator.value&&(0!==d||(b.elements[d].combinator.value||" ")!==(c.elements[d].combinator.value||" ")))return!1;if(!this.isElementValuesEqual(b.elements[d].value,c.elements[d].value))return!1}return!0}return!1},extendSelector:function(b,c,d){var e,f,g,h,i,j=0,k=0,l=[];for(e=0;e<b.length;e++)h=b[e],f=c[h.pathIndex],g=new a.Element(h.initialCombinator,d.elements[0].value,d.elements[0].index,d.elements[0].currentFileInfo),h.pathIndex>j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);
+return j<c.length&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i<e.length;i++)this._sourceMapGenerator.addMapping({generated:{line:this._lineNumber+i+1,column:0===i?this._column:0},original:{line:f.length+i,column:0===i?h.length:0},source:this.normalizeFilename(b.filename)});else this._sourceMapGenerator.addMapping({generated:{line:this._lineNumber+1,column:this._column},original:{line:f.length,column:h.length},source:this.normalizeFilename(b.filename)});1===e.length?this._column+=g.length:(this._lineNumber+=e.length-1,this._column=g.length),this._css.push(a)}},a.sourceMapOutput.prototype.isEmpty=function(){return 0===this._css.length},a.sourceMapOutput.prototype.toCSS=function(a){if(this._sourceMapGenerator=new this._sourceMapGeneratorConstructor({file:this._outputFilename,sourceRoot:null}),this._outputSourceFiles)for(var b in this._contentsMap)if(this._contentsMap.hasOwnProperty(b)){var c=this._contentsMap[b];this._contentsIgnoredCharsMap[b]&&(c=c.slice(this._contentsIgnoredCharsMap[b])),this._sourceMapGenerator.setSourceContent(this.normalizeFilename(b),c)}if(this._rootNode.genCSS(a,this),this._css.length>0){var d,e=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?d=this._sourceMapURL:this._sourceMapFilename&&(d=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(e):d="data:application/json,"+encodeURIComponent(e),d&&this._css.push("/*# sourceMappingURL="+d+" */")}return this._css.join("")}}(c("./tree"));var y=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);w.env=w.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||y?"development":"production");var z={debug:3,info:2,errors:1,none:0};if(w.logLevel="undefined"!=typeof w.logLevel?w.logLevel:"development"===w.env?z.debug:z.errors,w.async=w.async||!1,w.fileAsync=w.fileAsync||!1,w.poll=w.poll||(y?1e3:1500),w.functions)for(var A in w.functions)w.functions.hasOwnProperty(A)&&(w.tree.functions[A]=w.functions[A]);var B=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);B&&(w.dumpLineNumbers=B[1]);var C=/^text\/(x-)?less$/,D=null,E={};if(w.watch=function(){return w.watchMode||(w.env="development",v()),this.watchMode=!0,!0},w.unwatch=function(){return clearInterval(w.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&w.watch(),"development"!=w.env)try{D="undefined"==typeof a.localStorage?null:a.localStorage}catch(F){}var G=document.getElementsByTagName("link");w.sheets=[];for(var H=0;H<G.length;H++)("stylesheet/less"===G[H].rel||G[H].rel.match(/stylesheet/)&&G[H].type.match(C))&&w.sheets.push(G[H]);w.modifyVars=function(a){w.refresh(!1,a)},w.refresh=function(a,b){var c,e;c=e=new Date,u(function(a,b,f,i,k){if(a)return j(a,i.href);if(k.local)d("loading "+i.href+" from cache.",z.info);else{d("parsed "+i.href+" successfully.",z.debug);var l=b.toCSS(w);l=h(l),g(l,i,k.lastModified)}d("css for "+i.href+" generated in "+(new Date-e)+"ms",z.info),0===k.remaining&&d("less has finished. css generated in "+(new Date-c)+"ms",z.info),e=new Date},a,b),n(b)},w.refreshStyles=n,w.Parser.fileLoader=s,w.refresh("development"===w.env),"function"==typeof define&&define.amd&&define(function(){return w})}(window);
diff --git a/askbot/media/js/post.js b/askbot/media/js/post.js
index 54e5ed37..d077ce9e 100644
--- a/askbot/media/js/post.js
+++ b/askbot/media/js/post.js
@@ -2506,7 +2506,7 @@ var socialSharing = function(){
URL = window.location.href;
var urlBits = URL.split('/');
URL = urlBits.slice(0, -2).join('/') + '/';
- TEXT = encodeURIComponent($('h1 > a').html());
+ TEXT = encodeURIComponent($('h1 > a').text());
var hashtag = encodeURIComponent(
askbot['settings']['sharingSuffixText']
);
diff --git a/askbot/media/js/utils.js b/askbot/media/js/utils.js
index 64932ccd..a3eb2028 100644
--- a/askbot/media/js/utils.js
+++ b/askbot/media/js/utils.js
@@ -6,6 +6,21 @@ var mediaUrl = function(resource){
return askbot['settings']['static_url'] + 'default' + '/' + resource;
};
+var getCookie = function(name) {
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = $.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ return decodeURIComponent(cookie.substring(name.length + 1));
+ }
+ }
+ }
+ return cookieValue;
+};
+
var cleanUrl = function(url){
var re = new RegExp('//', 'g');
return url.replace(re, '/');
@@ -277,6 +292,7 @@ var notify = function() {
};
}();
+
/* **************************************************** */
// Search query-string manipulation utils
/* **************************************************** */
@@ -1374,6 +1390,12 @@ CommentConvertLink.prototype.createDom = function(){
hidden_input.attr('id', 'id_comment_id');
element.append(hidden_input);
+ var csrf_token = this.makeElement('input');
+ csrf_token.attr('type', 'hidden');
+ csrf_token.attr('name', 'csrfmiddlewaretoken');
+ csrf_token.attr('value', getCookie(askbot['settings']['csrfCookieName']));
+ element.append(csrf_token);
+
var submit = this.makeElement('input');
submit.attr('type', 'submit');
submit.attr('value', gettext('convert to answer'));
diff --git a/askbot/media/style/style.css b/askbot/media/style/style.css
index 395c52f8..d86b8fc7 100644
--- a/askbot/media/style/style.css
+++ b/askbot/media/style/style.css
@@ -330,6 +330,9 @@ body.user-messages {
background: white;
display: block;
}
+.select-language {
+ margin-top: 6px;
+}
#userToolsNav {
/* Navigation bar containing login link or user information, check widgets/user_navigation.html*/
@@ -586,19 +589,16 @@ body.user-messages {
-webkit-box-sizing: border-box;
background: whitesmoke;
border-top: none;
- bottom: -86px;
+ top: 42px;
margin: 0;
outline: 1px solid #dadad4;
position: absolute;
z-index: 10000;
}
-.search-drop-menu.empty {
- bottom: -50px;
-}
.search-drop-menu ul {
list-style: none;
overflow: auto;
- padding: 0 0 10px 0;
+ padding: 0;
margin: 0;
position: relative;
width: 100%;
@@ -619,7 +619,10 @@ body.user-messages {
.search-drop-menu .footer {
text-align: center;
margin: 0 0 1px 0;
- padding: 9px 0 10px 0;
+ padding: 4px 0 10px 0;
+}
+.search-drop-menu.empty .footer {
+ padding-top: 9px;
}
.search-drop-menu.empty ul {
padding: 1px;
@@ -668,13 +671,21 @@ input[type="submit"].searchBtn {
color: white;
height: 0;
z-index: 0;
+ position: absolute;
+ left: -1000px;
+ /* hide away */
+
+}
+.ask-page .search-drop-menu {
+ top: 37px;
}
.ask-page .search-drop-menu.empty {
border: none;
- padding: 0;
+ outline: none;
+ padding: 0 !important;
}
.ask-page .search-drop-menu.empty ul {
- padding: 0;
+ padding: 0 !important;
}
input[type="submit"].searchBtn:hover {
background-image: none;
@@ -1636,6 +1647,7 @@ ul#related-tags li {
padding: 4px 0 0 0;
margin-top: 0px;
width: 100%;
+ position: relative;
}
#askFormBar p {
margin: 0 0 5px 0;
@@ -1724,7 +1736,11 @@ ul#related-tags li {
}
.ask-page .lang-selector,
.edit-question-page .lang-selector {
- margin: 1px 0 0 5px;
+ margin: 9px 0 0 0;
+}
+.ask-page .lang-selector select,
+.edit-question-page .lang-selector select {
+ margin: 3px 0;
}
.ask-page #id_post_author_username,
.question-page #id_post_author_username,
@@ -2992,19 +3008,26 @@ a:hover.medal {
border: #eee 1px solid;
padding: 5px;
}
+.user-profile-page form {
+ margin-bottom: 0;
+}
.user-profile-page h2 {
- padding: 30px 0px 10px 0px;
+ padding: 4px 0px 10px 0px;
font-family: 'Open Sans Condensed', Arial, sans-serif;
}
+.user-profile-page h3 {
+ padding: 0;
+ margin-top: -3px;
+}
+.user-profile-page .submit-row {
+ margin-bottom: 0;
+}
.user-profile-page .user-stats-table h2 {
padding-top: 10px;
}
.user-profile-page .user-stats-table.badges {
margin-bottom: 30px;
}
-.user-profile-page select {
- margin-bottom: 12px;
-}
.user-profile-page .up-votes,
.user-profile-page .down-votes {
display: inline-block;
@@ -3035,6 +3058,18 @@ a:hover.medal {
.user-profile-page .users-questions {
margin-top: -10px;
}
+.user-profile-page .avatar p {
+ margin-bottom: 0px;
+}
+.user-profile-page .tabBar a#stats {
+ margin-left: 0;
+}
+.user-profile-page img.gravatar {
+ margin: 2px 0 3px 0;
+}
+.user-profile-page .vote-notification h3 {
+ padding: 10px;
+}
.user-details {
font-size: 13px;
}
@@ -3859,25 +3894,6 @@ p.signup_p {
.avatar-page li {
display: inline;
}
-.user-profile-page .avatar p {
- margin-bottom: 0px;
-}
-.user-profile-page .tabBar a#stats {
- margin-left: 0;
-}
-.user-profile-page img.gravatar {
- margin: 2px 0 3px 0;
-}
-.user-profile-page h3 {
- padding: 0;
- margin-top: -3px;
-}
-.user-profile-page .vote-notification h3 {
- padding: 10px;
-}
-.user-profile-page ul.tags {
- margin-left: 5px;
-}
.userList {
font-size: 13px;
}
@@ -4347,6 +4363,10 @@ td.setting-input textarea {
-o-transition: border linear 0.2s, box-shadow linear 0.2s;
transition: border linear 0.2s, box-shadow linear 0.2s;
}
+#recaptcha_area,
+#recaptcha_table {
+ line-height: 0!important;
+}
.acInput {
width: 200px;
}
diff --git a/askbot/media/style/style.less b/askbot/media/style/style.less
index 4b5dc93b..09b2810f 100644
--- a/askbot/media/style/style.less
+++ b/askbot/media/style/style.less
@@ -338,6 +338,9 @@ body.user-messages {
display: block;
}
}
+.select-language {
+ margin-top: 6px;
+}
#userToolsNav {/* Navigation bar containing login link or user information, check widgets/user_navigation.html*/
height: 20px;
@@ -619,20 +622,16 @@ body.user-messages {
-webkit-box-sizing: border-box;
background: whitesmoke;
border-top: none;
- bottom: -86px;
+ top: 42px;
margin: 0;
outline: 1px solid #dadad4;
position: absolute;
z-index: 10000;
- &.empty {
- bottom: -50px;
- }
-
ul {
list-style: none;
overflow: auto;
- padding: 0 0 10px 0;
+ padding: 0;
margin: 0;
position: relative;
width: 100%;
@@ -654,7 +653,10 @@ body.user-messages {
.footer {
text-align: center;
margin: 0 0 1px 0;
- padding: 9px 0 10px 0;
+ padding: 4px 0 10px 0;
+ }
+ &.empty .footer {
+ padding-top: 9px;
}
}
@@ -708,12 +710,18 @@ input[type="submit"].searchBtn {
color: white;
height: 0;
z-index: 0;
+ position: absolute;
+ left: -1000px;/* hide away */
}
- .search-drop-menu.empty {
- border: none;
- padding: 0;
- ul {
- padding: 0;
+ .search-drop-menu {
+ top: 37px;
+ &.empty {
+ border: none;
+ outline: none;
+ padding: 0 !important;
+ ul {
+ padding: 0 !important;
+ }
}
}
}
@@ -1737,6 +1745,7 @@ ul#related-tags li {
padding: 4px 0 0 0;
margin-top:0px;
width: 100%;
+ position: relative;
p {
margin:0 0 5px 0;
@@ -1823,7 +1832,10 @@ ul#related-tags li {
max-width: 395px;
}
.lang-selector {
- margin: 1px 0 0 5px;
+ margin: 9px 0 0 0;
+ select {
+ margin: 3px 0;
+ }
}
}
@@ -2929,13 +2941,13 @@ ul#related-tags li {
.users-page,
.user-profile-edit-page,
.user-profile-page {
- form{
+ form {
margin-bottom:15px;
}
input[type="text"],
input[type="password"],
- select{
+ select {
height:25px;
line-height: 25px;
padding-left:5px;
@@ -3127,7 +3139,7 @@ a:hover.medal {
/* profile page */
-.tabBar-profile{
+.tabBar-profile {
width:100%;
margin-bottom:5px;
float:left;
@@ -3136,7 +3148,7 @@ a:hover.medal {
.user-profile-page {
color:@info-text-dark;
- p{
+ p {
font-size:13px;
line-height:1.3;
color:@info-text-dark;
@@ -3146,11 +3158,24 @@ a:hover.medal {
padding:5px;
}
+ form {
+ margin-bottom: 0;
+ }
+
h2 {
- padding: 30px 0px 10px 0px;
+ padding: 4px 0px 10px 0px;
font-family: @main-font;
}
+ h3 {
+ padding: 0;
+ margin-top: -3px;
+ }
+
+ .submit-row {
+ margin-bottom: 0;
+ }
+
.user-stats-table h2 {
padding-top: 10px;
}
@@ -3159,9 +3184,6 @@ a:hover.medal {
margin-bottom: 30px;
}
- select {
- margin-bottom: 12px;
- }
.up-votes,
.down-votes {
display: inline-block;
@@ -3172,9 +3194,11 @@ a:hover.medal {
line-height: 22px;
margin: 0 15px 0 2px;
}
+
.up-votes {
background: url(../images/vote-arrow-up-on.png) no-repeat;
}
+
.down-votes {
background: url(../images/vote-arrow-down-on.png) no-repeat;
}
@@ -3193,6 +3217,19 @@ a:hover.medal {
.users-questions {
margin-top: -10px;
}
+
+ .avatar p {
+ margin-bottom: 0px;
+ }
+ .tabBar a#stats {
+ margin-left: 0;
+ }
+ img.gravatar {
+ margin: 2px 0 3px 0;
+ }
+ .vote-notification h3 {
+ padding: 10px;
+ }
}
.user-details {
@@ -4125,28 +4162,6 @@ p.signup_p {
display: inline;
}
-.user-profile-page {
- .avatar p {
- margin-bottom: 0px;
- }
- .tabBar a#stats {
- margin-left: 0;
- }
- img.gravatar {
- margin: 2px 0 3px 0;
- }
- h3 {
- padding: 0;
- margin-top: -3px;
- }
- .vote-notification h3 {
- padding: 10px;
- }
- ul.tags {
- margin-left: 5px;
- }
-}
-
.userList {
font-size: 13px;
}
@@ -4565,6 +4580,10 @@ td.setting-input {
}
}
+#recaptcha_area, #recaptcha_table {
+ line-height: 0!important;
+}
+
.acInput {
width: 200px;
}
diff --git a/askbot/middleware/remote_ip.py b/askbot/middleware/remote_ip.py
new file mode 100644
index 00000000..18280d0d
--- /dev/null
+++ b/askbot/middleware/remote_ip.py
@@ -0,0 +1,21 @@
+"""Fixes REMOTE_IP meta value
+based on the HTTP_X_FORWARDED_FOR value, if used.
+Enable this middleware if using django site behind a proxy
+server or a load balancer.
+
+Add to the MIDDLEWARE_CLASSES:
+
+ 'askbot.middleware.remote_ip.SetRemoteIPFromXForwardedFor',
+"""
+
+class SetRemoteIPFromXForwardedFor(object):
+ def process_request(self, request):
+ try:
+ real_ip = request.META['HTTP_X_FORWARDED_FOR']
+ except KeyError:
+ pass
+ else:
+ # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs.
+ # Take just the first one.
+ real_ip = real_ip.split(",")[0]
+ request.META['REMOTE_ADDR'] = real_ip
diff --git a/askbot/migrations/0055_auto__chg_field_question_thread.py b/askbot/migrations/0055_auto__chg_field_question_thread.py
index 9fa7885b..f0f04916 100644
--- a/askbot/migrations/0055_auto__chg_field_question_thread.py
+++ b/askbot/migrations/0055_auto__chg_field_question_thread.py
@@ -9,7 +9,7 @@ class Migration(SchemaMigration):
def forwards(self, orm):
# Changing field 'Question.thread'
- db.alter_column(u'question', 'thread_id', self.gf('django.db.models.fields.related.ForeignKey')(default=0, unique=True, to=orm['askbot.Thread']))
+ db.alter_column(u'question', 'thread_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, unique=True, to=orm['askbot.Thread']))
def backwards(self, orm):
diff --git a/askbot/migrations/0080_transplant_favquestions_2.py b/askbot/migrations/0080_transplant_favquestions_2.py
index 3edd95c3..81a8ea56 100644
--- a/askbot/migrations/0080_transplant_favquestions_2.py
+++ b/askbot/migrations/0080_transplant_favquestions_2.py
@@ -12,7 +12,7 @@ class Migration(SchemaMigration):
db.delete_column(u'favorite_question', 'question_id')
# Changing field 'FavoriteQuestion.thread'
- db.alter_column(u'favorite_question', 'thread_id', self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['askbot.Thread']))
+ db.alter_column(u'favorite_question', 'thread_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['askbot.Thread']))
def backwards(self, orm):
diff --git a/askbot/migrations/0093_auto__del_field_vote_content_type__del_field_vote_object_id__chg_field.py b/askbot/migrations/0093_auto__del_field_vote_content_type__del_field_vote_object_id__chg_field.py
index 830a3cd6..ccbb3e25 100644
--- a/askbot/migrations/0093_auto__del_field_vote_content_type__del_field_vote_object_id__chg_field.py
+++ b/askbot/migrations/0093_auto__del_field_vote_content_type__del_field_vote_object_id__chg_field.py
@@ -18,7 +18,7 @@ class Migration(SchemaMigration):
db.delete_column(u'vote', 'object_id')
# Changing field 'Vote.voted_post'
- db.alter_column(u'vote', 'voted_post_id', self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['askbot.Post']))
+ db.alter_column(u'vote', 'voted_post_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['askbot.Post']))
# Adding unique constraint on 'Vote', fields ['user', 'voted_post']
db.create_unique(u'vote', ['user_id', 'voted_post_id'])
diff --git a/askbot/migrations/0099_auto__del_field_thread_accepted_answer__del_field_anonymousanswer_ques.py b/askbot/migrations/0099_auto__del_field_thread_accepted_answer__del_field_anonymousanswer_ques.py
index ac9507a5..36dd4850 100644
--- a/askbot/migrations/0099_auto__del_field_thread_accepted_answer__del_field_anonymousanswer_ques.py
+++ b/askbot/migrations/0099_auto__del_field_thread_accepted_answer__del_field_anonymousanswer_ques.py
@@ -21,13 +21,13 @@ class Migration(SchemaMigration):
db.delete_column('askbot_anonymousanswer', 'question_id')
# Changing field 'AnonymousAnswer.question_post'
- db.alter_column('askbot_anonymousanswer', 'question_post_id', self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['askbot.Post']))
+ db.alter_column('askbot_anonymousanswer', 'question_post_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['askbot.Post']))
# Deleting field 'QuestionView.question'
db.delete_column('askbot_questionview', 'question_id')
# Changing field 'QuestionView.question_post'
- db.alter_column('askbot_questionview', 'question_post_id', self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['askbot.Post']))
+ db.alter_column('askbot_questionview', 'question_post_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['askbot.Post']))
# Deleting field 'PostRevision.question'
db.delete_column('askbot_postrevision', 'question_id')
diff --git a/askbot/migrations/0172_sync_thread_deleted.py b/askbot/migrations/0172_sync_thread_deleted.py
new file mode 100644
index 00000000..961df840
--- /dev/null
+++ b/askbot/migrations/0172_sync_thread_deleted.py
@@ -0,0 +1,419 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ for q in orm.Post.objects.filter(post_type='question', deleted=False,
+ thread__deleted=True):
+ q.thread.deleted = False
+ q.thread.save()
+
+ def backwards(self, orm):
+ 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': u"orm['contenttypes.ContentType']"}),
+ u'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.Post']", 'null': 'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': u"orm['auth.User']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': u"orm['auth.User']"}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ u'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': u"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': u"orm['auth.User']", 'null': 'True'}),
+ u'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.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ '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': u"orm['auth.User']", 'null': 'True'}),
+ u'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'}),
+ '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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ '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': u"orm['contenttypes.ContentType']"}),
+ u'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': u"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': u"orm['auth.User']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.bulktagsubscription': {
+ 'Meta': {'ordering': "['-date_added']", 'object_name': 'BulkTagSubscription'},
+ 'date_added': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Group']", 'symmetrical': 'False'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Tag']", 'symmetrical': 'False'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': u"orm['auth.User']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", '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'}),
+ u'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': u"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'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': u"orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': [u'auth.Group']},
+ 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ u'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'is_vip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'read_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ u'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.importedobjectinfo': {
+ 'Meta': {'object_name': 'ImportedObjectInfo'},
+ 'extra_info': ('picklefield.fields.PickledObjectField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'new_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'old_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'run': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.ImportRun']"})
+ },
+ 'askbot.importrun': {
+ 'Meta': {'object_name': 'ImportRun'},
+ 'command': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ u'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': u"orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': u"orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': u"orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ '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_posts'", 'null': 'True', 'to': u"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_posts'", 'null': 'True', 'to': u"orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}),
+ '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.postflagreason': {
+ 'Meta': {'object_name': 'PostFlagReason'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
+ 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'askbot.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': u"orm['auth.User']"}),
+ 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ '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', [], {'default': "''", 'max_length': '125', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'})
+ },
+ 'askbot.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': u"orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ u'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.Post']", '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': u"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': u"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': u"orm['auth.User']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': u"orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.tagsynonym': {
+ 'Meta': {'object_name': 'TagSynonym'},
+ 'auto_rename_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_auto_rename_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'owned_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_synonyms'", 'to': u"orm['auth.User']"}),
+ 'source_tag_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'target_tag_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'askbot.thread': {
+ 'Meta': {'object_name': 'Thread'},
+ 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ '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', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", 'symmetrical': 'False', 'through': "orm['askbot.FavoriteQuestion']", 'to': u"orm['auth.User']"}),
+ 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_threads'", 'symmetrical': 'False', 'to': u"orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': u"orm['auth.User']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': u"orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.Group']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
+ },
+ u'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ u'auth.permission': {
+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ u'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_signature': ('django.db.models.fields.TextField', [], {'blank': '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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'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_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'languages': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '128'}),
+ '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'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'social_sharing_mode': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'twitter_access_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
+ 'twitter_handle': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ u'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'}),
+ u'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']
+ symmetrical = True
diff --git a/askbot/migrations/0173_auto__del_unique_tag_name__chg_field_thread_added_at.py b/askbot/migrations/0173_auto__del_unique_tag_name__chg_field_thread_added_at.py
new file mode 100644
index 00000000..0550c79d
--- /dev/null
+++ b/askbot/migrations/0173_auto__del_unique_tag_name__chg_field_thread_added_at.py
@@ -0,0 +1,426 @@
+# -*- coding: 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):
+ # Removing unique constraint on 'Tag', fields ['name']
+ db.delete_unique(u'tag', ['name'])
+
+
+ # Changing field 'Thread.added_at'
+ db.alter_column('askbot_thread', 'added_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True))
+
+ def backwards(self, orm):
+ # Adding unique constraint on 'Tag', fields ['name']
+ db.create_unique(u'tag', ['name'])
+
+
+ # Changing field 'Thread.added_at'
+ db.alter_column('askbot_thread', 'added_at', self.gf('django.db.models.fields.DateTimeField')())
+
+ 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.Post']", '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.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ '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'}),
+ '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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ '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'})
+ },
+ 'askbot.bulktagsubscription': {
+ 'Meta': {'ordering': "['-date_added']", 'object_name': 'BulkTagSubscription'},
+ 'date_added': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Tag']", 'symmetrical': 'False'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", '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'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'is_vip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'read_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.importedobjectinfo': {
+ 'Meta': {'object_name': 'ImportedObjectInfo'},
+ 'extra_info': ('picklefield.fields.PickledObjectField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'new_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'old_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'run': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.ImportRun']"})
+ },
+ 'askbot.importrun': {
+ 'Meta': {'object_name': 'ImportRun'},
+ 'command': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ '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.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ '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_posts'", '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}),
+ '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.postflagreason': {
+ 'Meta': {'object_name': 'PostFlagReason'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'askbot.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}),
+ 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ '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', [], {'default': "''", 'max_length': '125', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'})
+ },
+ 'askbot.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ '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.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'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.Post']", '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', [], {'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.tagsynonym': {
+ 'Meta': {'object_name': 'TagSynonym'},
+ 'auto_rename_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_auto_rename_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'owned_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_synonyms'", 'to': "orm['auth.User']"}),
+ 'source_tag_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'target_tag_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'askbot.thread': {
+ 'Meta': {'object_name': 'Thread'},
+ 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ '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', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", '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_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': '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'}),
+ '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_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'languages': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '128'}),
+ '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'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'social_sharing_mode': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'twitter_access_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
+ 'twitter_handle': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32'}),
+ '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': '255'}),
+ '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'] \ No newline at end of file
diff --git a/askbot/migrations/0174_auto__add_field_tag_language_code.py b/askbot/migrations/0174_auto__add_field_tag_language_code.py
new file mode 100644
index 00000000..bac3dd7b
--- /dev/null
+++ b/askbot/migrations/0174_auto__add_field_tag_language_code.py
@@ -0,0 +1,423 @@
+# -*- coding: 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):
+ # Adding field 'Tag.language_code'
+ db.add_column(u'tag', 'language_code',
+ self.gf('django.db.models.fields.CharField')(default='en', max_length=16),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Tag.language_code'
+ db.delete_column(u'tag', 'language_code')
+
+
+ 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.Post']", '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.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ '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'}),
+ '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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ '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'})
+ },
+ 'askbot.bulktagsubscription': {
+ 'Meta': {'ordering': "['-date_added']", 'object_name': 'BulkTagSubscription'},
+ 'date_added': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Tag']", 'symmetrical': 'False'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", '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'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'is_vip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'read_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.importedobjectinfo': {
+ 'Meta': {'object_name': 'ImportedObjectInfo'},
+ 'extra_info': ('picklefield.fields.PickledObjectField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'new_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'old_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'run': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.ImportRun']"})
+ },
+ 'askbot.importrun': {
+ 'Meta': {'object_name': 'ImportRun'},
+ 'command': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ '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.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ '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_posts'", '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}),
+ '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.postflagreason': {
+ 'Meta': {'object_name': 'PostFlagReason'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'askbot.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}),
+ 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ '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', [], {'default': "''", 'max_length': '125', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'})
+ },
+ 'askbot.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ '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.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'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.Post']", '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.tagsynonym': {
+ 'Meta': {'object_name': 'TagSynonym'},
+ 'auto_rename_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_auto_rename_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'owned_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_synonyms'", 'to': "orm['auth.User']"}),
+ 'source_tag_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'target_tag_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'askbot.thread': {
+ 'Meta': {'object_name': 'Thread'},
+ 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ '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', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", '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_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': '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'}),
+ '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_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'languages': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '128'}),
+ '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'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'social_sharing_mode': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'twitter_access_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
+ 'twitter_handle': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32'}),
+ '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': '255'}),
+ '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'] \ No newline at end of file
diff --git a/askbot/migrations/0175_auto__add_unique_tag_name_language_code.py b/askbot/migrations/0175_auto__add_unique_tag_name_language_code.py
new file mode 100644
index 00000000..daa404af
--- /dev/null
+++ b/askbot/migrations/0175_auto__add_unique_tag_name_language_code.py
@@ -0,0 +1,421 @@
+# -*- coding: 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):
+ # Adding unique constraint on 'Tag', fields ['name', 'language_code']
+ db.create_unique(u'tag', ['name', 'language_code'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'Tag', fields ['name', 'language_code']
+ db.delete_unique(u'tag', ['name', 'language_code'])
+
+
+ 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.Post']", '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.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ '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'}),
+ '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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ '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'})
+ },
+ 'askbot.bulktagsubscription': {
+ 'Meta': {'ordering': "['-date_added']", 'object_name': 'BulkTagSubscription'},
+ 'date_added': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Tag']", 'symmetrical': 'False'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", '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'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'is_vip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'read_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.importedobjectinfo': {
+ 'Meta': {'object_name': 'ImportedObjectInfo'},
+ 'extra_info': ('picklefield.fields.PickledObjectField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'new_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'old_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'run': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.ImportRun']"})
+ },
+ 'askbot.importrun': {
+ 'Meta': {'object_name': 'ImportRun'},
+ 'command': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ '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.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ '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_posts'", '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}),
+ '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.postflagreason': {
+ 'Meta': {'object_name': 'PostFlagReason'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'askbot.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}),
+ 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ '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', [], {'default': "''", 'max_length': '125', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'})
+ },
+ 'askbot.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ '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.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'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.Post']", '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')", 'unique_together': "(('name', 'language_code'),)", '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.tagsynonym': {
+ 'Meta': {'object_name': 'TagSynonym'},
+ 'auto_rename_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_auto_rename_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'owned_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_synonyms'", 'to': "orm['auth.User']"}),
+ 'source_tag_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'target_tag_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'askbot.thread': {
+ 'Meta': {'object_name': 'Thread'},
+ 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ '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', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", '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_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': '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'}),
+ '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_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'languages': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '128'}),
+ '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'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'social_sharing_mode': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'twitter_access_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
+ 'twitter_handle': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32'}),
+ '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': '255'}),
+ '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'] \ No newline at end of file
diff --git a/askbot/migrations/0176_populate_tag_language_code.py b/askbot/migrations/0176_populate_tag_language_code.py
new file mode 100644
index 00000000..6c9f0aa7
--- /dev/null
+++ b/askbot/migrations/0176_populate_tag_language_code.py
@@ -0,0 +1,464 @@
+# -*- coding: utf-8 -*-
+import datetime
+from askbot.utils.console import ProgressBar
+from south.db import db
+from south.v2 import DataMigration
+from django.conf import settings as django_settings
+from django.db import models
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ # Note: Don't use "from appname.models import ModelName".
+ # Use orm.ModelName to refer to models in this application,
+ # and orm['appname.ModelName'] for models in other applications.
+ threads = orm['askbot.Thread'].objects.all()
+ count = threads.count()
+ message = 'Applying language code to tags:'
+ for thread in ProgressBar(threads.iterator(), count, message):
+ tags = thread.tags.all()
+ #load each tag by name + language code
+ for tag in tags:
+ #maybe fix language code of thread, unlikely
+ if thread.language_code == '':
+ thread.language_code = django_settings.LANGUAGE_CODE
+ thread.save()
+
+ #if tg has no language code, we just copy it from thread
+ if tag.language_code == '':
+ tag.language_code = thread.language_code
+ tag.save()
+ elif tag.language_code != thread.language_code:
+ try:
+ new_tag = orm['askbot.Tag'].objects.get(
+ name=tag.name,
+ language_code=thread.language_code
+ )
+ if tags.filter(id=new_tag.id).exists():
+ continue
+ new_tag.used_count += 1
+
+ except orm['askbot.Tag'].DoesNotExist:
+ new_tag = orm['askbot.Tag']()
+ new_tag.name = tag.name
+ new_tag.language_code = thread.language_code
+ new_tag.created_by = thread.last_activity_by
+ new_tag.used_count = 1
+ new_tag.status = 1
+ new_tag.save()
+ new_tag.suggested_by.add(thread.last_activity_by)
+
+ if tag.used_count > 1:
+ tag.used_count -= 1
+ tag.save()
+ thread.tags.remove(tag)
+ thread.tags.add(new_tag)
+
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+
+ 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.Post']", '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.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ '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'}),
+ '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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ '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'})
+ },
+ 'askbot.bulktagsubscription': {
+ 'Meta': {'ordering': "['-date_added']", 'object_name': 'BulkTagSubscription'},
+ 'date_added': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Tag']", 'symmetrical': 'False'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", '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'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'is_vip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'read_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.importedobjectinfo': {
+ 'Meta': {'object_name': 'ImportedObjectInfo'},
+ 'extra_info': ('picklefield.fields.PickledObjectField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'new_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'old_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'run': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.ImportRun']"})
+ },
+ 'askbot.importrun': {
+ 'Meta': {'object_name': 'ImportRun'},
+ 'command': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ '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.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ '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_posts'", '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}),
+ '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.postflagreason': {
+ 'Meta': {'object_name': 'PostFlagReason'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'askbot.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}),
+ 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ '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', [], {'default': "''", 'max_length': '125', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'})
+ },
+ 'askbot.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ '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.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'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.Post']", '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')", 'unique_together': "(('name', 'language_code'),)", '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.tagsynonym': {
+ 'Meta': {'object_name': 'TagSynonym'},
+ 'auto_rename_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_auto_rename_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'owned_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_synonyms'", 'to': "orm['auth.User']"}),
+ 'source_tag_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'target_tag_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'askbot.thread': {
+ 'Meta': {'object_name': 'Thread'},
+ 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ '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', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", '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_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': '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'}),
+ '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_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'languages': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '128'}),
+ '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'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'social_sharing_mode': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'twitter_access_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
+ 'twitter_handle': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32'}),
+ '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': '255'}),
+ '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']
+ symmetrical = True
diff --git a/askbot/migrations/0177_auto__add_field_tagsynonym_language_code.py b/askbot/migrations/0177_auto__add_field_tagsynonym_language_code.py
new file mode 100644
index 00000000..1a9d38c8
--- /dev/null
+++ b/askbot/migrations/0177_auto__add_field_tagsynonym_language_code.py
@@ -0,0 +1,424 @@
+# -*- coding: 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):
+ # Adding field 'TagSynonym.language_code'
+ db.add_column('askbot_tagsynonym', 'language_code',
+ self.gf('django.db.models.fields.CharField')(default='en', max_length=16),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'TagSynonym.language_code'
+ db.delete_column('askbot_tagsynonym', 'language_code')
+
+
+ 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.Post']", '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.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ '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'}),
+ '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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ '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'})
+ },
+ 'askbot.bulktagsubscription': {
+ 'Meta': {'ordering': "['-date_added']", 'object_name': 'BulkTagSubscription'},
+ 'date_added': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Tag']", 'symmetrical': 'False'}),
+ 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", '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'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'is_vip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'read_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.importedobjectinfo': {
+ 'Meta': {'object_name': 'ImportedObjectInfo'},
+ 'extra_info': ('picklefield.fields.PickledObjectField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
+ 'new_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'old_id': ('django.db.models.fields.IntegerField', [], {}),
+ 'run': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.ImportRun']"})
+ },
+ 'askbot.importrun': {
+ 'Meta': {'object_name': 'ImportRun'},
+ 'command': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ '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.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ '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_posts'", '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}),
+ '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.postflagreason': {
+ 'Meta': {'object_name': 'PostFlagReason'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'askbot.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}),
+ 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ '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', [], {'default': "''", 'max_length': '125', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'})
+ },
+ 'askbot.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ '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.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'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.Post']", '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')", 'unique_together': "(('name', 'language_code'),)", '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'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.tagsynonym': {
+ 'Meta': {'object_name': 'TagSynonym'},
+ 'auto_rename_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'last_auto_rename_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'owned_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_synonyms'", 'to': "orm['auth.User']"}),
+ 'source_tag_name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'target_tag_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'askbot.thread': {
+ 'Meta': {'object_name': 'Thread'},
+ 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ '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', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", '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_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'language_code': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '16'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': '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'}),
+ '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_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'languages': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '128'}),
+ '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'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'social_sharing_mode': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'twitter_access_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256'}),
+ 'twitter_handle': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32'}),
+ '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': '255'}),
+ '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'] \ No newline at end of file
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 09d10f3f..93c8d599 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -23,6 +23,7 @@ from django.core.paginator import Paginator
from django.db.models import signals as django_signals
from django.template import Context
from django.template.loader import get_template
+from django.utils.translation import get_language
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
from django.utils.safestring import mark_safe
@@ -98,8 +99,7 @@ def get_admin():
if User.objects.filter(username='_admin_').count() == 0:
admin = User.objects.create_user('_admin_', '')
admin.set_unusable_password()
- admin.set_admin_status()
- admin.save()
+ admin.set_status('d')
return admin
else:
raise User.DoesNotExist
@@ -265,13 +265,14 @@ User.add_to_class(
)
)
-GRAVATAR_TEMPLATE = "//www.gravatar.com/avatar/%(gravatar)s?" + \
+GRAVATAR_TEMPLATE = "%(gravatar_url)s/%(gravatar)s?" + \
"s=%(size)d&amp;d=%(type)s&amp;r=PG"
def user_get_gravatar_url(self, size):
"""returns gravatar url
"""
return GRAVATAR_TEMPLATE % {
+ 'gravatar_url': askbot_settings.GRAVATAR_BASE_URL,
'gravatar': self.gravatar,
'type': askbot_settings.GRAVATAR_TYPE,
'size': size,
@@ -306,11 +307,10 @@ def user_get_avatar_url(self, size=48):
raise django_exceptions.ImproperlyConfigured(message)
else:
return self.get_gravatar_url(size)
+ if askbot_settings.ENABLE_GRAVATAR:
+ return self.get_gravatar_url(size)
else:
- if askbot_settings.ENABLE_GRAVATAR:
- return self.get_gravatar_url(size)
- else:
- return self.get_default_avatar_url(size)
+ return self.get_default_avatar_url(size)
def user_get_top_answers_paginator(self, visitor=None):
"""get paginator for top answers by the user for a
@@ -355,7 +355,10 @@ def user_strip_email_signature(self, text):
return text
def _check_gravatar(gravatar):
- gravatar_url = "http://www.gravatar.com/avatar/%s?d=404" % gravatar
+ return 'n'
+ #todo: think of whether we need this and if so
+ #how to check the avatar type appropriately
+ gravatar_url = askbot_settings.GRAVATAR_BASE_URL + "/%s?d=404" % gravatar
code = urllib.urlopen(gravatar_url).getcode()
if urllib.urlopen(gravatar_url).getcode() != 404:
return 'g' #gravatar
@@ -384,8 +387,9 @@ def user_get_marked_tags(self, reason):
return Tag.objects.none()
return Tag.objects.filter(
- user_selections__user = self,
- user_selections__reason = reason
+ user_selections__user=self,
+ user_selections__reason=reason,
+ language_code=get_language()
)
MARKED_TAG_PROPERTY_MAP = {
@@ -539,6 +543,20 @@ def user_get_or_create_fake_user(self, username, email):
user.save()
return user
+def get_or_create_anonymous_user():
+ """returns fake anonymous user"""
+ username = get_name_of_anonymous_user()
+ try:
+ user = User.objects.get(username=username)
+ except User.DoesNotExist:
+ user = User()
+ user.username = username
+ user.email = askbot_settings.ANONYMOUS_USER_EMAIL
+ user.is_fake = True
+ user.set_unusable_password()
+ user.save()
+ return user
+
def user_notify_users(
self, notification_type=None, recipients=None, content_object=None
):
@@ -834,6 +852,20 @@ def user_assert_can_edit_comment(self, comment = None):
)
raise django_exceptions.PermissionDenied(error_message)
+def user_assert_can_convert_post(self, post = None):
+ """raises exceptions.PermissionDenied if user is not allowed to convert the
+ post to another type (comment -> answer, answer -> comment)
+
+ only owners, moderators or admins can convert posts
+ """
+ if self.is_administrator() or self.is_moderator() or post.author == self:
+ return
+
+ error_message = _(
+ 'Sorry, but only post owners or moderators convert posts'
+ )
+ raise django_exceptions.PermissionDenied(error_message)
+
def user_can_post_comment(self, parent_post = None):
"""a simplified method to test ability to comment
@@ -1290,20 +1322,27 @@ def user_mark_tags(
tagnames = list()
#figure out which tags don't yet exist
+ language_code = get_language()
existing_tagnames = Tag.objects.filter(
- name__in=tagnames
+ name__in=tagnames,
+ language_code=language_code
).values_list(
'name', flat=True
)
non_existing_tagnames = set(tagnames) - set(existing_tagnames)
#create those tags, and if tags are moderated make them suggested
if (len(non_existing_tagnames) > 0):
- Tag.objects.create_in_bulk(tag_names=tagnames, user=self)
+ Tag.objects.create_in_bulk(
+ tag_names=tagnames,
+ user=self,
+ language_code=language_code
+ )
#below we update normal tag selections
marked_ts = MarkedTag.objects.filter(
user = self,
- tag__name__in = tagnames
+ tag__name__in=tagnames,
+ tag__language_code=language_code
)
#Marks for "good" and "bad" reasons are exclusive,
#to make it impossible to "like" and "dislike" something at the same time
@@ -1325,7 +1364,10 @@ def user_mark_tags(
marked_names = marked_ts.values_list('tag__name', flat = True)
if len(marked_names) < len(tagnames):
unmarked_names = set(tagnames).difference(set(marked_names))
- ts = Tag.objects.filter(name__in = unmarked_names)
+ ts = Tag.objects.filter(
+ name__in=unmarked_names,
+ language_code=language_code
+ )
new_marks = list()
for tag in ts:
MarkedTag(
@@ -1372,7 +1414,7 @@ def user_repost_comment_as_answer(self, comment):
parent question"""
#todo: add assertion
- #self.assert_can_repost_comment_as_answer(comment)
+ self.assert_can_convert_post(comment)
comment.post_type = 'answer'
old_parent = comment.parent
@@ -1569,6 +1611,9 @@ def user_restore_post(
post.deleted_by = None
post.deleted_at = None
post.save()
+ if post.post_type == 'question':
+ post.thread.deleted = False
+ post.thread.save()
post.thread.invalidate_cached_data()
if post.post_type == 'answer':
post.thread.update_answer_count()
@@ -2238,11 +2283,14 @@ def user_get_tag_filtered_questions(self, questions = None):
if questions is None:
questions = Post.objects.get_questions()
+ language_code = get_language()
+
if self.email_tag_filter_strategy == const.EXCLUDE_IGNORED:
ignored_tags = Tag.objects.filter(
user_selections__reason = 'bad',
- user_selections__user = self
+ user_selections__user = self,
+ language_code=language_code
)
wk = self.ignored_tags.strip().split()
@@ -2263,7 +2311,8 @@ def user_get_tag_filtered_questions(self, questions = None):
selected_tags = Tag.objects.filter(
user_selections__reason = reason,
- user_selections__user = self
+ user_selections__user = self,
+ language_code=language_code
)
selected_by_wildcards = Tag.objects.get_by_wildcards(wk)
@@ -2330,11 +2379,12 @@ def user_get_primary_group(self):
first non-personal non-everyone group
works only for one real private group per-person
"""
- groups = self.get_groups(private=True)
- for group in groups:
- if group.is_personal():
- continue
- return group
+ if askbot_settings.GROUPS_ENABLED:
+ groups = self.get_groups(private=True)
+ for group in groups:
+ if group.is_personal():
+ continue
+ return group
return None
def user_can_make_group_private_posts(self):
@@ -2710,11 +2760,13 @@ def user_update_response_counts(user):
def user_receive_reputation(self, num_points):
- new_points = self.reputation + num_points
+ old_points = self.reputation
+ new_points = old_points + num_points
if new_points > 0:
self.reputation = new_points
else:
self.reputation = const.MIN_REPUTATION
+ signals.reputation_received.send(None, user=self, reputation_before=old_points)
def user_update_wildcard_tag_selections(
self,
@@ -2977,6 +3029,8 @@ User.add_to_class('assert_can_delete_post', user_assert_can_delete_post)
User.add_to_class('assert_can_restore_post', user_assert_can_restore_post)
User.add_to_class('assert_can_delete_comment', user_assert_can_delete_comment)
User.add_to_class('assert_can_edit_comment', user_assert_can_edit_comment)
+User.add_to_class('assert_can_convert_post', user_assert_can_convert_post)
+
User.add_to_class('assert_can_delete_answer', user_assert_can_delete_answer)
User.add_to_class('assert_can_delete_question', user_assert_can_delete_question)
User.add_to_class('assert_can_accept_best_answer', user_assert_can_accept_best_answer)
@@ -3073,7 +3127,14 @@ def format_instant_notification_email(
post_url = site_url(post.get_absolute_url())
user_url = site_url(from_user.get_absolute_url())
- if post.is_anonymous:
+ if to_user.is_administrator_or_moderator() and askbot_settings.SHOW_ADMINS_PRIVATE_USER_DATA:
+ user_link_fmt = '<a href="%(profile_url)s">%(username)s</a> (<a href="mailto:%(email)s">%(email)s</a>)'
+ user_link = user_link_fmt % {
+ 'profile_url': user_url,
+ 'username': from_user.username,
+ 'email': from_user.email
+ }
+ elif post.is_anonymous:
user_link = from_user.get_name_of_anonymous_user()
else:
user_link = '<a href="%s">%s</a>' % (user_url, from_user.username)
@@ -3637,8 +3698,7 @@ def make_admin_if_first_user(user, **kwargs):
import sys
user_count = User.objects.all().count()
if user_count == 1:
- user.set_admin_status()
- user.save()
+ user.set_status('d')
def moderate_group_joining(sender, instance=None, created=False, **kwargs):
if created and instance.level == GroupMembership.PENDING:
@@ -3656,6 +3716,13 @@ def tweet_new_post(sender, user=None, question=None, answer=None, form_data=None
post = question or answer
tweet_new_post_task.delay(post.id)
+def autoapprove_reputable_user(user=None, reputation_before=None, *args, **kwargs):
+ """if user is 'watched' we change status to 'approved'
+ if user's rep crossed the auto-approval margin"""
+ margin = askbot_settings.MIN_REP_TO_AUTOAPPROVE_USER
+ if user.is_watched() and reputation_before < margin and user.reputation >= margin:
+ user.set_status('a')
+
def init_badge_data(sender, created_models=None, **kwargs):
if BadgeData in created_models:
from askbot.models import badges
@@ -3678,7 +3745,7 @@ django_signals.post_save.connect(moderate_group_joining, sender=GroupMembership)
if 'avatar' in django_settings.INSTALLED_APPS:
from avatar.models import Avatar
- django_signals.post_save.connect(set_user_avatar_type_flag,sender=Avatar)
+ django_signals.post_save.connect(set_user_avatar_type_flag, sender=Avatar)
django_signals.post_delete.connect(update_user_avatar_type_flag, sender=Avatar)
django_signals.post_delete.connect(record_cancel_vote, sender=Vote)
@@ -3696,6 +3763,7 @@ signals.user_logged_in.connect(post_anonymous_askbot_content)
signals.post_updated.connect(record_post_update_activity)
signals.new_answer_posted.connect(tweet_new_post)
signals.new_question_posted.connect(tweet_new_post)
+signals.reputation_received.connect(autoapprove_reputable_user)
#probably we cannot use post-save here the point of this is
#to tell when the revision becomes publicly visible, not when it is saved
diff --git a/askbot/models/post.py b/askbot/models/post.py
index 7ab0b524..47af2a42 100644
--- a/askbot/models/post.py
+++ b/askbot/models/post.py
@@ -377,7 +377,11 @@ class Post(models.Model):
html = models.TextField(null=True)#html rendition of the latest revision
text = models.TextField(null=True)#denormalized copy of latest revision
- language_code = models.CharField(max_length=16, default=django_settings.LANGUAGE_CODE)
+ language_code = models.CharField(
+ choices=django_settings.LANGUAGES,
+ default=django_settings.LANGUAGE_CODE,
+ max_length=16,
+ )
# Denormalised data
summary = models.TextField(null=True)
@@ -1091,6 +1095,7 @@ class Post(models.Model):
tag_names = self.get_tag_names()
tag_selections = MarkedTag.objects.filter(
tag__name__in = tag_names,
+ tag__language_code=get_language(),
reason = tag_mark_reason
)
subscribers = set(
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 5455b927..65e1168a 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -4,6 +4,7 @@ import re
from django.conf import settings as django_settings
from django.db import models
+from django.db.models import F
from django.contrib.auth.models import User
from django.core import cache # import cache, not from cache import cache, to be able to monkey-patch cache.cache in test cases
from django.core import exceptions as django_exceptions
@@ -24,7 +25,8 @@ from askbot.models.tag import Tag, TagSynonym
from askbot.models.tag import get_tags_by_names
from askbot.models.tag import filter_accepted_tags, filter_suggested_tags
from askbot.models.tag import separate_unused_tags
-from askbot.models.base import DraftContent, BaseQuerySetManager
+from askbot.models.base import BaseQuerySetManager
+from askbot.models.base import DraftContent
from askbot.models.user import Group, PERSONAL_GROUP_NAME_PREFIX
from askbot.models import signals
from askbot import const
@@ -122,6 +124,8 @@ class ThreadManager(BaseQuerySetManager):
tag_list.sort(key=lambda t: tag_counts[t], reverse=True)
#note that double quote placement is important here
+ if len(tag_list) == 0:
+ return ''
if len(tag_list) == 1:
last_topic = '"'
elif len(tag_list) <= 5:
@@ -398,19 +402,23 @@ class ThreadManager(BaseQuerySetManager):
if request_user and request_user.is_authenticated():
#mark questions tagged with interesting tags
#a kind of fancy annotation, would be nice to avoid it
+ lang = get_language()
interesting_tags = Tag.objects.filter(
- user_selections__user = request_user,
- user_selections__reason = 'good'
+ user_selections__user=request_user,
+ user_selections__reason='good',
+ language_code=lang
)
ignored_tags = Tag.objects.filter(
user_selections__user = request_user,
- user_selections__reason = 'bad'
+ user_selections__reason = 'bad',
+ language_code=lang
)
subscribed_tags = Tag.objects.none()
if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED:
subscribed_tags = Tag.objects.filter(
user_selections__user = request_user,
- user_selections__reason = 'subscribed'
+ user_selections__reason = 'subscribed',
+ language_code=lang
)
meta_data['subscribed_tag_names'] = [tag.name for tag in subscribed_tags]
@@ -584,7 +592,11 @@ class Thread(models.Model):
answer_count = models.PositiveIntegerField(default=0)
last_activity_at = models.DateTimeField(default=datetime.datetime.now)
last_activity_by = models.ForeignKey(User, related_name='unused_last_active_in_threads')
- language_code = models.CharField(max_length=16, default=django_settings.LANGUAGE_CODE)
+ language_code = models.CharField(
+ choices=django_settings.LANGUAGES,
+ default=django_settings.LANGUAGE_CODE,
+ max_length=16
+ )
#todo: these two are redundant (we used to have a "star" and "subscribe"
#now merged into "followed")
@@ -608,7 +620,7 @@ class Thread(models.Model):
accepted_answer = models.ForeignKey('Post', null=True, blank=True, related_name='+')
answer_accepted_at = models.DateTimeField(null=True, blank=True)
- added_at = models.DateTimeField(default = datetime.datetime.now)
+ added_at = models.DateTimeField(auto_now_add=True)
#db_column will be removed later
points = models.IntegerField(default = 0, db_column='score')
@@ -818,6 +830,65 @@ class Thread(models.Model):
self.save()
self.invalidate_cached_data()
+ def set_tags_language_code(self, language_code=None):
+ """sets language code to tags of this thread.
+ If lang code of the tag does not coincide with that
+ of thread, we replace the tag with the one of correct
+ lang code. If necessary, tags are created and
+ the used_counts are updated.
+ """
+ wrong_lang_tags = list()
+ for tag in self.tags.all():
+ if tag.language_code != language_code:
+ wrong_lang_tags.append(tag)
+
+ #remove wrong tags
+ self.tags.remove(*wrong_lang_tags)
+ #update used counts of the wrong tags
+ wrong_lang_tag_names = list()
+ for tag in wrong_lang_tags:
+ wrong_lang_tag_names.append(tag.name)
+ if tag.used_count > 0:
+ tag.used_count -= 1
+ tag.save()
+
+ #load existing tags and figure out which tags don't exist
+ reused_tags, new_tagnames = get_tags_by_names(
+ wrong_lang_tag_names,
+ language_code=language_code
+ )
+ reused_tags.mark_undeleted()
+ #tag moderation is in the call below
+ created_tags = Tag.objects.create_in_bulk(
+ language_code=self.language_code,
+ tag_names=new_tagnames,
+ user=self.last_activity_by,
+ auto_approve=True
+ )
+ #add the tags
+ added_tags = list(reused_tags) + list(created_tags)
+ self.tags.add(*added_tags)
+ #increment the used counts and save tags
+ tag_ids = [tag.id for tag in added_tags]
+ Tag.objects.filter(id__in=tag_ids).update(used_count=F('used_count') + 1)
+
+ def set_language_code(self, language_code=None):
+ assert(language_code)
+
+ #save language code on thread
+ self.language_code = language_code
+ self.save()
+
+ #save language code on all posts
+ #for some reason "update" fails in postgres - possibly b/c of the FTS
+ for post in self.posts.all():
+ post.language_code = language_code
+ post.save()
+
+ #make sure that tags have correct language code
+ self.set_tags_language_code(language_code)
+
+
def set_accepted_answer(self, answer, timestamp):
if answer and answer.thread != self:
raise ValueError("Answer doesn't belong to this thread")
@@ -840,16 +911,13 @@ class Thread(models.Model):
else:
return self.tagnames.split(u' ')
- def get_title(self, question=None):
- if not question:
- question = self._question_post() # allow for optimization if the caller has already fetched the question post for this thread
+ def get_title(self):
if self.is_private():
attr = const.POST_STATUS['private']
elif self.closed:
attr = const.POST_STATUS['closed']
- elif question.deleted:
+ elif self.deleted:
attr = const.POST_STATUS['deleted']
-
else:
attr = None
if attr is not None:
@@ -1118,7 +1186,8 @@ class Thread(models.Model):
# we had question post id denormalized on the thread
tags_list = self.get_tag_names()
similar_threads = Thread.objects.filter(
- tags__name__in=tags_list
+ tags__name__in=tags_list,
+ language_code=self.language_code
).exclude(
id = self.id
).exclude(
@@ -1153,7 +1222,7 @@ class Thread(models.Model):
# this is a "legacy" problem inherited from the old models
if question_post:
url = question_post.get_absolute_url()
- title = thread.get_title(question_post)
+ title = thread.get_title()
result.append({'url': url, 'title': title})
return result
@@ -1301,7 +1370,7 @@ class Thread(models.Model):
def update_tags(
- self, tagnames = None, user = None, timestamp = None
+ self, tagnames=None, user=None, timestamp=None
):
"""
Updates Tag associations for a thread to match the given
@@ -1329,7 +1398,10 @@ class Thread(models.Model):
updated_tagnames = set()
for tag_name in updated_tagnames_tmp:
try:
- tag_synonym = TagSynonym.objects.get(source_tag_name=tag_name)
+ tag_synonym = TagSynonym.objects.get(
+ source_tag_name=tag_name,
+ language_code=self.language_code
+ )
updated_tagnames.add(tag_synonym.target_tag_name)
tag_synonym.auto_rename_count += 1
tag_synonym.save()
@@ -1353,14 +1425,18 @@ class Thread(models.Model):
if added_tagnames:
#find reused tags
- reused_tags, new_tagnames = get_tags_by_names(added_tagnames)
+ reused_tags, new_tagnames = get_tags_by_names(
+ added_tagnames,
+ language_code=self.language_code
+ )
reused_tags.mark_undeleted()
added_tags = list(reused_tags)
#tag moderation is in the call below
created_tags = Tag.objects.create_in_bulk(
+ language_code=self.language_code,
tag_names=new_tagnames,
- user=user
+ user=user,
)
added_tags.extend(created_tags)
diff --git a/askbot/models/signals.py b/askbot/models/signals.py
index 42a9c787..00152a0a 100644
--- a/askbot/models/signals.py
+++ b/askbot/models/signals.py
@@ -52,6 +52,7 @@ post_revision_published = django.dispatch.Signal(
]
)
site_visited = django.dispatch.Signal(providing_args=['user', 'timestamp'])
+reputation_received = django.dispatch.Signal(providing_args=['user', 'reputation_before'])
def pop_signal_receivers(signal):
"""disables a given signal by removing listener functions
diff --git a/askbot/models/tag.py b/askbot/models/tag.py
index bf054627..1934f81a 100644
--- a/askbot/models/tag.py
+++ b/askbot/models/tag.py
@@ -1,9 +1,10 @@
import re
from django.db import models
from django.contrib.auth.models import User
+from django.utils.translation import get_language
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
-from django.conf import settings
+from django.conf import settings as django_settings
from askbot.models.base import BaseQuerySetManager
from askbot import const
from askbot.conf import settings as askbot_settings
@@ -12,13 +13,16 @@ from askbot.utils import category_tree
def delete_tags(tags):
"""deletes tags in the list"""
tag_ids = [tag.id for tag in tags]
- Tag.objects.filter(id__in = tag_ids).delete()
+ Tag.objects.filter(id__in=tag_ids).delete()
-def get_tags_by_names(tag_names):
+def get_tags_by_names(tag_names, language_code=None):
"""returns query set of tags
and a set of tag names that were not found
"""
- tags = Tag.objects.filter(name__in = tag_names)
+ tags = Tag.objects.filter(
+ name__in=tag_names,
+ language_code=language_code
+ )
#if there are brand new tags, create them
#and finalize the added tag list
if tags.count() < len(tag_names):
@@ -155,7 +159,7 @@ class TagQuerySet(models.query.QuerySet):
tag_filter = models.Q(name__startswith = first_tag[:-1])
for next_tag in wildcards:
tag_filter |= models.Q(name__startswith = next_tag[:-1])
- return self.filter(tag_filter)
+ return self.filter(tag_filter & models.Q(language_code=get_language()))
def get_related_to_search(self, threads, ignored_tag_names):
"""Returns at least tag names, along with use counts"""
@@ -177,9 +181,9 @@ class TagManager(BaseQuerySetManager):
"""temporary function that filters out the group tags"""
return self.all()
- def create(self, name=None, created_by=None, **kwargs):
+ def create(self, name=None, created_by=None, auto_approve=False, **kwargs):
"""Creates a new tag"""
- if created_by.can_create_tags() or is_preapproved_tag_name(name):
+ if auto_approve or created_by.can_create_tags() or is_preapproved_tag_name(name):
status = Tag.STATUS_ACCEPTED
else:
status = Tag.STATUS_SUGGESTED
@@ -219,7 +223,7 @@ class TagManager(BaseQuerySetManager):
) % ', '.join(tag_names)
user.message_set.create(message = msg)
- def create_in_bulk(self, tag_names = None, user = None):
+ def create_in_bulk(self, tag_names=None, user=None, language_code=None, auto_approve=False):
"""creates tags by names. If user can create tags,
then they are set status ``STATUS_ACCEPTED``,
otherwise the status will be set to ``STATUS_SUGGESTED``.
@@ -227,21 +231,24 @@ class TagManager(BaseQuerySetManager):
One exception: if suggested tag is in the category tree
and source of tags is category tree - then status of newly
created tag is ``STATUS_ACCEPTED``
+ if `auto_approve` is True then tags are auto-accepted
"""
#load suggested tags
pre_suggested_tags = self.filter(
- name__in = tag_names, status = Tag.STATUS_SUGGESTED
+ name__in=tag_names,
+ status=Tag.STATUS_SUGGESTED,
+ language_code=language_code
)
#deal with suggested tags
- if user.can_create_tags():
+ if auto_approve or user.can_create_tags():
#turn previously suggested tags into accepted
pre_suggested_tags.update(status = Tag.STATUS_ACCEPTED)
else:
#increment use count and add user to "suggested_by"
for tag in pre_suggested_tags:
- tag.times_used += 1
+ tag.used_count += 1
tag.suggested_by.add(user)
tag.save()
@@ -253,7 +260,12 @@ class TagManager(BaseQuerySetManager):
for tag_name in set(tag_names) - set(pre_suggested_tag_names):
#status for the new tags is automatically set within the create()
- new_tag = Tag.objects.create(name = tag_name, created_by = user)
+ new_tag = Tag.objects.create(
+ name=tag_name,
+ created_by=user,
+ language_code=language_code,
+ auto_approve=auto_approve
+ )
created_tags.append(new_tag)
if new_tag.status == Tag.STATUS_SUGGESTED:
@@ -273,9 +285,13 @@ class Tag(models.Model):
STATUS_SUGGESTED = 0
STATUS_ACCEPTED = 1
- name = models.CharField(max_length=255, unique=True)
+ name = models.CharField(max_length=255)
created_by = models.ForeignKey(User, related_name='created_tags')
-
+ language_code = models.CharField(
+ choices=django_settings.LANGUAGES,
+ default=django_settings.LANGUAGE_CODE,
+ max_length=16,
+ )
suggested_by = models.ManyToManyField(
User, related_name='suggested_tags',
help_text = 'Works only for suggested tags for tag moderation'
@@ -302,6 +318,7 @@ class Tag(models.Model):
app_label = 'askbot'
db_table = u'tag'
ordering = ('-used_count', 'name')
+ unique_together = ('name', 'language_code')
def __unicode__(self):
return self.name
@@ -328,6 +345,11 @@ class TagSynonym(models.Model):
owned_by = models.ForeignKey(User, related_name='tag_synonyms')
auto_rename_count = models.IntegerField(default=0)
last_auto_rename_at = models.DateTimeField(auto_now=True)
+ language_code = models.CharField(
+ choices=django_settings.LANGUAGES,
+ default=django_settings.LANGUAGE_CODE,
+ max_length=16,
+ )
class Meta:
app_label = 'askbot'
diff --git a/askbot/models/user.py b/askbot/models/user.py
index a3e2d4f0..5501f30f 100644
--- a/askbot/models/user.py
+++ b/askbot/models/user.py
@@ -9,6 +9,7 @@ from django.contrib.auth.models import User
from django.contrib.auth.models import Group as AuthGroup
from django.core import exceptions
from django.forms import EmailField, URLField
+from django.utils import translation
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from django.utils.html import strip_tags
@@ -634,9 +635,13 @@ class Group(AuthGroup):
class BulkTagSubscriptionManager(BaseQuerySetManager):
def create(
- self, tag_names=None,
- user_list=None, group_list=None,
- tag_author=None, **kwargs
+ self,
+ tag_names=None,
+ user_list=None,
+ group_list=None,
+ tag_author=None,
+ language_code=None,
+ **kwargs
):
tag_names = tag_names or []
@@ -648,7 +653,7 @@ class BulkTagSubscriptionManager(BaseQuerySetManager):
if tag_names:
from askbot.models.tag import get_tags_by_names
- tags, new_tag_names = get_tags_by_names(tag_names)
+ tags, new_tag_names = get_tags_by_names(tag_names, language_code)
if new_tag_names:
assert(tag_author)
@@ -657,9 +662,10 @@ class BulkTagSubscriptionManager(BaseQuerySetManager):
from askbot.models.tag import Tag
new_tags = Tag.objects.create_in_bulk(
- tag_names=new_tag_names,
- user=tag_author
- )
+ tag_names=new_tag_names,
+ user=tag_author,
+ language_code=translation.get_language()
+ )
tags_id_list.extend([tag.id for tag in new_tags])
tag_name_list.extend([tag.name for tag in new_tags])
diff --git a/askbot/patches/__init__.py b/askbot/patches/__init__.py
index 6145097c..947e1816 100644
--- a/askbot/patches/__init__.py
+++ b/askbot/patches/__init__.py
@@ -20,6 +20,14 @@ def patch_django():
if major == 1 and minor <=2:
django_patches.add_render_shortcut()
+ if major == 1 and minor > 4:
+ # This shouldn't be required with django < 1.4.x
+ # And not after kee_lazy lands in django.utils.functional
+ try:
+ from django.utils.functional import keep_lazy
+ except ImportError:
+ django_patches.fix_lazy_double_escape()
+
def patch_coffin():
"""coffin before version 0.3.4
does not have csrf_token template tag.
diff --git a/askbot/patches/django_patches.py b/askbot/patches/django_patches.py
index fe0e2fe7..5907d794 100644
--- a/askbot/patches/django_patches.py
+++ b/askbot/patches/django_patches.py
@@ -352,3 +352,33 @@ def add_render_shortcut():
import django.shortcuts
django.shortcuts.render = render
+
+
+from django.utils import six
+from django.utils.functional import Promise
+import django.utils.html
+
+def fix_lazy_double_escape():
+ """
+ Wrap django.utils.html.escape to fix the double escape issue visible at
+ least with field labels with localization
+ """
+ django.utils.html.escape = wrap_escape(django.utils.html.escape)
+
+
+def wrap_escape(func):
+ """
+ Decorator adapted from https://github.com/django/django/pull/1007
+ """
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ for arg in list(args) + list(six.itervalues(kwargs)):
+ if isinstance(arg, Promise):
+ break
+ else:
+ return func(*args, **kwargs)
+ return lazy(func, six.text_type)(*args, **kwargs)
+ @wraps(wrapper)
+ def wrapped(*args, **kwargs):
+ return mark_safe(func(*args, **kwargs))
+ return wrapped
diff --git a/askbot/schedules.py b/askbot/schedules.py
deleted file mode 100644
index b9bbdbc8..00000000
--- a/askbot/schedules.py
+++ /dev/null
@@ -1,19 +0,0 @@
-"""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/setup_templates/urls.py b/askbot/setup_templates/urls.py
index eb04e836..22847a3f 100644
--- a/askbot/setup_templates/urls.py
+++ b/askbot/setup_templates/urls.py
@@ -2,10 +2,14 @@
main url configuration file for the askbot site
"""
from django.conf import settings
-from django.conf.urls.defaults import handler404
-from django.conf.urls.defaults import include
-from django.conf.urls.defaults import patterns
-from django.conf.urls.defaults import url
+try:
+ from django.conf.urls import handler404
+ from django.conf.urls import include, patterns, url
+except ImportError:
+ from django.conf.urls.defaults import handler404
+ from django.conf.urls.defaults import include, patterns, url
+
+from askbot.views.error import internal_error as handler500
from django.conf import settings
from django.contrib import admin
diff --git a/askbot/templates/answer_edit.html b/askbot/templates/answer_edit.html
index c9d8a147..c49f3ccd 100644
--- a/askbot/templates/answer_edit.html
+++ b/askbot/templates/answer_edit.html
@@ -21,6 +21,10 @@
editor_type = settings.EDITOR_TYPE
)
}}
+ {% if form.recaptcha %}
+ <div>{{ macros.form_field_with_errors(form.recaptcha) }}</div>
+ <div class="clearfix"></div>
+ {% endif %}
<div class="answer-options">
{% if settings.WIKI_ON and answer.wiki == False %}
{{ macros.checkbox_in_div(form.wiki) }}
@@ -40,12 +44,11 @@
id="edit_post_form_submit_button"
type="submit"
value="{% trans %}Save edit{% endtrans %}"
- class="submit"
/>&nbsp;
<input
type="button"
value="{% trans %}Cancel{% endtrans %}"
- class="submit cancel"
+ class="cancel"
onclick="history.back(-1);"
/>
</div>
diff --git a/askbot/templates/authopenid/complete.html b/askbot/templates/authopenid/complete.html
index c41f0a0f..cefb1941 100644
--- a/askbot/templates/authopenid/complete.html
+++ b/askbot/templates/authopenid/complete.html
@@ -63,11 +63,15 @@ anyone, must be valid</i>)
{% endif %}
{{ openid_register_form.email }}
</div>
+ {% if openid_register_form.recaptcha %}
+ <div class="form-row-vertical">
+ {{ macros.form_field_with_errors(openid_register_form.recaptcha ) }}
+ </div>
+ {% endif %}
<div class="submit-row">
<input
id="register-button"
type="submit"
- class="submit"
name="bnewaccount"
value="{% trans %}Signup{% endtrans %}"
/>
diff --git a/askbot/templates/authopenid/providers_javascript.html b/askbot/templates/authopenid/providers_javascript.html
index 86f1004f..d20f052f 100644
--- a/askbot/templates/authopenid/providers_javascript.html
+++ b/askbot/templates/authopenid/providers_javascript.html
@@ -1,11 +1,14 @@
<script type='text/javascript' src='{{ "/js/jquery.validate.min.js"|media }}'></script>
+{% if settings.SIGNIN_MOZILLA_PERSONA_ENABLED %}
+<script type="text/javascript" src="https://login.persona.org/include.js"></script>
+{% endif %}
<script type="text/javascript" src="{{ "/jquery-openid/jquery.openid.js"|media }}"></script>
<script type="text/javascript">
askbot['urls']['changePassword'] = '{% url change_password %}';
+ askbot['urls']['deleteLoginMethod'] = '{% url delete_login_method %}';
var extra_token_name = {};
var create_pw_text = {};
var change_pw_text = {};
- var authUrl = '/{% trans %}account/{% endtrans %}';
var siteName = '{{settings.APP_SHORT_NAME}}';
var provider_count = {{existing_login_methods|length}};
{% for login_provider in major_login_providers %}
diff --git a/askbot/templates/authopenid/signin.html b/askbot/templates/authopenid/signin.html
index daf5a45a..c2717023 100644
--- a/askbot/templates/authopenid/signin.html
+++ b/askbot/templates/authopenid/signin.html
@@ -44,6 +44,7 @@
{% endif %}
{{ login_form.login_provider_name }}
{{ login_form.next }}
+ {{ login_form.persona_assertion }}
{{
login_macros.provider_buttons(
login_form = login_form,
diff --git a/askbot/templates/authopenid/signup_with_password.html b/askbot/templates/authopenid/signup_with_password.html
index c3625b62..ef8d8572 100644
--- a/askbot/templates/authopenid/signup_with_password.html
+++ b/askbot/templates/authopenid/signup_with_password.html
@@ -38,9 +38,8 @@ your login details with anyone and having to remember yet another password.{% en
<li><label for="password1_id">{{form.password1.label}}</label>{{form.password1}}{{form.password1.errors}}</li>
<li><label for="password2_id">{{form.password2.label}}</label>{{form.password2}}{{form.password2.errors}}</li>
</ul>
- {% if settings.USE_RECAPTCHA %}
- <p class="signup_p">{% trans %}Please read and type in the two words below to help us prevent automated account creation.{% endtrans %}</p>
- {{form.recaptcha}}
+ {% if form.recaptcha %}
+ {{ main_macros.form_field_with_errors(form.recaptcha) }}
{% endif %}
<div class="submit-row"><input id="signup-button" type="submit" class="button" value="{% trans %}Signup{% endtrans %}" />
{% if settings.PASSWORD_REGISTER_SHOW_PROVIDER_BUTTONS == False %}
diff --git a/askbot/templates/authopenid/verify_email.html b/askbot/templates/authopenid/verify_email.html
index 613ca589..1312658b 100644
--- a/askbot/templates/authopenid/verify_email.html
+++ b/askbot/templates/authopenid/verify_email.html
@@ -8,7 +8,7 @@
</label>
<form method="post">{% csrf_token %}
<input id="validation-code" type="text" name="validation_code" />
- <input type="submit" class="submit" value="{% trans %}Confirm email{% endtrans %}" />
+ <input type="submit" value="{% trans %}Confirm email{% endtrans %}" />
</form>
{% endblock %}
<!-- end changeemail.html -->
diff --git a/askbot/templates/close.html b/askbot/templates/close.html
index 70d7cc8b..b0c239e4 100644
--- a/askbot/templates/close.html
+++ b/askbot/templates/close.html
@@ -12,8 +12,8 @@
{{ form.reason }}
</p>
<p>
- <input type="submit" value="{% trans %}OK to close{% endtrans %}" class="submit" />&nbsp;
- <input id="btBack" type="button" class="submit" value="{% trans %}Cancel{% endtrans %}" />
+ <input type="submit" value="{% trans %}OK to close{% endtrans %}"/>
+ <input id="btBack" type="button" value="{% trans %}Cancel{% endtrans %}"/>
</p>
</form>
{% endblock %}
diff --git a/askbot/templates/email/change_settings_info.html b/askbot/templates/email/change_settings_info.html
index 55bd729f..0f96caa4 100644
--- a/askbot/templates/email/change_settings_info.html
+++ b/askbot/templates/email/change_settings_info.html
@@ -5,7 +5,6 @@
{% else %}
{% trans %}To change freqency and content of these alerts, please visit <a href="{{ url }}">your user profile</a>.{% endtrans %}
{% endif %}
-</p>
-<p style="font-size:10px; font-style:italic;">
+ <br/>
{% trans %}If you believe that this message was sent in an error, please email about it the forum administrator at <a href="mailto:{{ admin_email }}">{{ admin_email }}</a>.{% endtrans %}
</p>
diff --git a/askbot/templates/email/macros.html b/askbot/templates/email/macros.html
index 4e23a6c6..c5b7cfcc 100644
--- a/askbot/templates/email/macros.html
+++ b/askbot/templates/email/macros.html
@@ -22,38 +22,38 @@
{% endif %}
{% if post.post_type == 'question' %}
- {% if quote_level > 0 %}
- <p style="font-size:10px; font-weight: bold;">
- {% else %}
- <p style="font-size:20px; font-weight: bold; margin: 10px 0">
- {% endif %}
- {% if format == 'parent_subthread' %}
- {% if is_leaf_post %}
- {% trans %}Started by {{ author }}:{% endtrans %}
+ {% if quote_level > 0 %}
+ <p style="font-size:10px; font-weight: bold;">
+ {% else %}
+ <p style="font-size:20px; font-weight: bold; margin: 10px 0">
+ {% endif %}
+ {% if format == 'parent_subthread' %}
+ {% if is_leaf_post %}
+ {% trans %}Started by {{ author }}:{% endtrans %}
+ {% else %}
+ {% trans -%}
+ In reply to {{ author }}:
+ {%- endtrans %}
+ {% endif %}
{% else %}
- {% trans -%}
- In reply to {{ author }}:
- {%- endtrans %}
+ {{ settings.WORDS_QUESTION_SINGULAR|title|escape }} :
{% endif %}
- {% else %}
- {{ settings.WORDS_QUESTION_SINGULAR|title|escape }} :
- {% endif %}
- {{ post.thread.title }}
- </p>
- {% if quote_level > 0 %}
- <p style="font-size:10px; font-weight: bold;">
- {% if format != 'parent_subthread' %}
- {% trans %}Started by {{ author }}:{% endtrans %}
- {% endif %}
- </p>
- {% endif %}
- {% set tag_names = post.get_tag_names() %}
- {% if tag_names %}
- <p style="font-size:10px; font-style:italic;">
- {% trans %}Tags:{% endtrans %}
- {{ tag_names|join(', ') }}.
- </p>
- {% endif %}
+ {{ post.thread.title }}
+ </p>
+ {% if quote_level > 0 %}
+ <p style="font-size:10px; font-weight: bold;">
+ {% if format != 'parent_subthread' %}
+ {% trans %}Started by {{ author }}:{% endtrans %}
+ {% endif %}
+ </p>
+ {% endif %}
+ {% set tag_names = post.get_tag_names() %}
+ {% if tag_names %}
+ <p style="font-size:10px; font-style:italic;">
+ {% trans %}Tags:{% endtrans %}
+ {{ tag_names|join(', ') }}.
+ </p>
+ {% endif %}
{% elif post.post_type == 'answer' %}
<p style="font-size:10px; font-weight: bold;">
{% if format == 'parent_subthread' %}
diff --git a/askbot/templates/embed/widget_form.html b/askbot/templates/embed/widget_form.html
index ad1562aa..a03aba29 100644
--- a/askbot/templates/embed/widget_form.html
+++ b/askbot/templates/embed/widget_form.html
@@ -11,7 +11,7 @@
{{ form.as_table() }}
<tr>
<td colspan="2" style="text-align: center">
- <input type="submit" class="submit" value={% trans %}Save{% endtrans %} />
+ <input type="submit" value={% trans %}Save{% endtrans %} />
</td>
</tr>
</table>
diff --git a/askbot/templates/feedback.html b/askbot/templates/feedback.html
index 04b9a5b4..040566c6 100644
--- a/askbot/templates/feedback.html
+++ b/askbot/templates/feedback.html
@@ -1,4 +1,5 @@
{% extends "two_column_body.html" %}
+{% import "macros.html" as macros %}
<!-- template feedback.html -->
{% block title %}{% spaceless %}{% trans %}Feedback{% endtrans %}{% endspaceless %}{% endblock %}
{% block content %}
@@ -51,17 +52,13 @@
</div>
{% if form.recaptcha %}
<div class="form-row">
- {% if form.errors.recaptcha%}
- <span class="error">{% trans %}(Please solve the captcha){% endtrans %}</span>
- </label>
- {% endif %}
- {{form.recaptcha}}
+ {{ macros.form_field_with_errors(form.recaptcha) }}
</div>
{% endif %}
{{form.next}}
<div class="submit-row">
- <input type="submit" class="submit" value="{% trans %}Send Feedback{% endtrans %}"/>&nbsp;
- <input type="submit" class="submit cancel" name="cancel" value="{% trans %}Cancel{% endtrans %}"/>
+ <input type="submit" value="{% trans %}Send Feedback{% endtrans %}"/>&nbsp;
+ <input type="submit" class="cancel" name="cancel" value="{% trans %}Cancel{% endtrans %}"/>
</div>
</form>
{% endblock %}
diff --git a/askbot/templates/macros.html b/askbot/templates/macros.html
index a17c808e..b314255c 100644
--- a/askbot/templates/macros.html
+++ b/askbot/templates/macros.html
@@ -388,7 +388,8 @@ for the purposes of the AJAX comment editor #}
show_comment = None,
show_comment_position = None,
user=None,
- max_comments=None
+ max_comments=None,
+ csrf_token=None
)
-%}
{% spaceless %}
@@ -451,7 +452,7 @@ for the purposes of the AJAX comment editor #}
accept-charset="utf-8"
class='convert-comment'
>
- {% csrf_token %}
+ <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
<input type="hidden" value="{{comment.id}}" name="comment_id" id="id_comment_id">
<input type="submit" value="{% trans %}convert to answer{% endtrans %}">
</form>
@@ -643,7 +644,7 @@ for the purposes of the AJAX comment editor #}
{%- macro user_full_location(user) -%}
{% if user.location %}
- {{ user.location }},
+ {{ user.location|escape }},
{% endif %}
{{ user_country_name_and_flag(user) }}
{%- endmacro -%}
@@ -847,3 +848,10 @@ for the purposes of the AJAX comment editor #}
})();
</script>
{% endmacro %}
+
+{% macro form_field_with_errors(field) %}
+ {% if field.errors %}
+ <p class="error">{{ field.errors|join(", ") }}</p>
+ {% endif %}
+ {{ field }}
+{% endmacro %}
diff --git a/askbot/templates/main_page/javascript.html b/askbot/templates/main_page/javascript.html
index 72d63ed3..3a3c3d23 100644
--- a/askbot/templates/main_page/javascript.html
+++ b/askbot/templates/main_page/javascript.html
@@ -6,10 +6,6 @@
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'] = '{% url mark_interesting_tag %}';
diff --git a/askbot/templates/meta/bottom_scripts.html b/askbot/templates/meta/bottom_scripts.html
index 3f1a6e57..c027258e 100644
--- a/askbot/templates/meta/bottom_scripts.html
+++ b/askbot/templates/meta/bottom_scripts.html
@@ -43,6 +43,7 @@
{% endif %}
askbot['data']['haveFlashNotifications'] = {{ user_messages|as_js_bool }};
askbot['data']['activeTab'] = '{{ active_tab }}';
+ askbot['settings']['csrfCookieName'] = '{{ settings.CSRF_COOKIE_NAME }}';
{% if search_state %}
askbot['data']['searchUrl'] = '{{ search_state.query_string()|escapejs }}';
{% else %}
diff --git a/askbot/templates/meta/fonts.html b/askbot/templates/meta/fonts.html
index 1e0fe707..4332e1a6 100644
--- a/askbot/templates/meta/fonts.html
+++ b/askbot/templates/meta/fonts.html
@@ -10,6 +10,6 @@
</style>
{% else %}
{# note: for IE8 we ask for fonts separately #}
- <link href='//fonts.googleapis.com/css?family=Open+Sans+Condensed:700&amp;subset=latin-ext' rel='stylesheet' type='text/css'>
- <link href='//fonts.googleapis.com/css?family=Open+Sans+Condensed:700&amp;subset=cyrillic-ext' rel='stylesheet' type='text/css'>
+ <link href='//fonts.googleapis.com/css?family=Open+Sans+Condensed:700&amp;subset=latin-ext' rel='stylesheet' type='text/css' />
+ <link href='//fonts.googleapis.com/css?family=Open+Sans+Condensed:700&amp;subset=cyrillic-ext' rel='stylesheet' type='text/css' />
{% endif %}
diff --git a/askbot/templates/meta/html_head_javascript.html b/askbot/templates/meta/html_head_javascript.html
index 062a435f..da9c7f74 100644
--- a/askbot/templates/meta/html_head_javascript.html
+++ b/askbot/templates/meta/html_head_javascript.html
@@ -7,12 +7,14 @@
{% if request.user.is_authenticated() %}
askbot['data']['userId'] = {{ request.user.id }};
askbot['data']['userName'] = '{{ request.user.username|escape }}';
+ askbot['data']['userEmail'] = '{{ request.user.email|escape }}';
askbot['data']['userIsAdminOrMod'] = {{ request.user.is_administrator_or_moderator()|as_js_bool }};
askbot['data']['userIsAdmin'] = {{ request.user.is_administrator()|as_js_bool }};
askbot['data']['userReputation'] = {{ request.user.reputation }};
askbot['data']['userIsReadOnly'] = {{ request.user.is_read_only()|as_js_bool }};
{% else %}
askbot['data']['userReputation'] = 0;
+ askbot['data']['userEmail'] = null;
askbot['data']['userIsReadOnly'] = false;//in principle we allow anon users to start posting
{% endif %}
askbot['urls'] = {};
diff --git a/askbot/templates/question/answer_comments.html b/askbot/templates/question/answer_comments.html
index e6b5e1c5..c0a0dfd8 100644
--- a/askbot/templates/question/answer_comments.html
+++ b/askbot/templates/question/answer_comments.html
@@ -5,6 +5,7 @@
show_comment = show_comment,
show_comment_position = show_comment_position,
user = request.user,
- max_comments = settings.MAX_COMMENTS_TO_SHOW
+ max_comments = settings.MAX_COMMENTS_TO_SHOW,
+ csrf_token = csrf_token
)
}}
diff --git a/askbot/templates/question/new_answer_form.html b/askbot/templates/question/new_answer_form.html
index f8b4cd46..9e8644d4 100644
--- a/askbot/templates/question/new_answer_form.html
+++ b/askbot/templates/question/new_answer_form.html
@@ -41,6 +41,10 @@
editor_type = settings.EDITOR_TYPE
)
}}
+ {% if answer.recaptcha %}
+ <div>{{ macros.form_field_with_errors(answer.recaptcha) }}</div>
+ <div class="clearfix"></div>
+ {% endif %}
<input id="add-answer-btn" type="submit" class="submit after-editor" style="float:left"/>
<script type="text/javascript">
askbot['functions']['renderAddAnswerButton']();
diff --git a/askbot/templates/question/question_card.html b/askbot/templates/question/question_card.html
index edaaeb06..d6c37260 100644
--- a/askbot/templates/question/question_card.html
+++ b/askbot/templates/question/question_card.html
@@ -5,7 +5,7 @@
{% include "question/share_buttons.html" %}
</div>
<div id="post-id-{{question.id}}" class="post-content{% if question.deleted %} deleted{% endif %}">
- <h1><a href="{{ question.get_absolute_url() }}">{{ thread.get_title(question)|escape }}</a></h1>
+ <h1><a href="{{ question.get_absolute_url() }}">{{ thread.get_title()|escape }}</a></h1>
{% include "question/question_tags.html" %}
<div class="clearfix"></div>
diff --git a/askbot/templates/question/question_comments.html b/askbot/templates/question/question_comments.html
index e9d3f724..a05f6328 100644
--- a/askbot/templates/question/question_comments.html
+++ b/askbot/templates/question/question_comments.html
@@ -5,6 +5,7 @@
show_comment = show_comment,
show_comment_position = show_comment_position,
user = request.user,
- max_comments = settings.MAX_COMMENTS_TO_SHOW
+ max_comments = settings.MAX_COMMENTS_TO_SHOW,
+ csrf_token = csrf_token
)
}}
diff --git a/askbot/templates/question_edit.html b/askbot/templates/question_edit.html
index e31050ec..f6de6bdf 100644
--- a/askbot/templates/question_edit.html
+++ b/askbot/templates/question_edit.html
@@ -35,6 +35,10 @@
)
}}
<div class="after-editor">
+ {% if form.recaptcha %}
+ <div>{{ macros.form_field_with_errors(form.recaptcha) }}</div>
+ <div class="clearfix"></div>
+ {% endif %}
<div class="question-options">
{% if settings.WIKI_ON and question.wiki == False %}
{{ macros.checkbox_in_div(form.wiki) }}
@@ -58,7 +62,7 @@
</div>
{% endif %}
</div>
- <input id="edit_post_form_submit_button" type="submit" value="{% trans %}Save edit{% endtrans %}" class="large submit" />&nbsp;
+ <input id="edit_post_form_submit_button" type="submit" value="{% trans %}Save edit{% endtrans %}" class="large submit" />
<input type="button" value="{% trans %}Cancel{% endtrans %}" class="large submit" onclick="history.back(-1);" />
</div>
diff --git a/askbot/templates/question_retag.html b/askbot/templates/question_retag.html
index e1341f7a..df8dc584 100644
--- a/askbot/templates/question_retag.html
+++ b/askbot/templates/question_retag.html
@@ -18,8 +18,8 @@
</div>
</div>
<div class="error" ></div>
- <input type="submit" value="{% trans %}Retag{% endtrans %}" class="submit" />&nbsp;
- <input type="button" value="{% trans %}Cancel{% endtrans %}" class="submit" onclick="history.back(-1);" />
+ <input type="submit" value="{% trans %}Retag{% endtrans %}"/>&nbsp;
+ <input type="button" value="{% trans %}Cancel{% endtrans %}" onclick="history.back(-1);" />
</form>
{% endblock %}
diff --git a/askbot/templates/reopen.html b/askbot/templates/reopen.html
index 9c617403..07208793 100644
--- a/askbot/templates/reopen.html
+++ b/askbot/templates/reopen.html
@@ -22,8 +22,8 @@
<p>{{ settings.WORDS_REOPEN_QUESTION|escape }}?</p>
<form id="fmclose" action="{% url reopen question.id %}" method="post" >{% csrf_token %}
<div id="" style="padding:20px 0 20px 0">
- <input type="submit" value="{{ settings.WORDS_REOPEN_QUESTION|escape }}" class="submit" />&nbsp;
- <input id="btBack" type="button" value="{% trans %}Cancel{% endtrans %}" class="submit" />
+ <input type="submit" value="{{ settings.WORDS_REOPEN_QUESTION|escape }}"/>
+ <input id="btBack" type="button" value="{% trans %}Cancel{% endtrans %}"/>
</div>
</form>
{% endblock %}
diff --git a/askbot/templates/tags/form_bulk_tag_subscription.html b/askbot/templates/tags/form_bulk_tag_subscription.html
index 95168e45..b6e72a96 100644
--- a/askbot/templates/tags/form_bulk_tag_subscription.html
+++ b/askbot/templates/tags/form_bulk_tag_subscription.html
@@ -9,7 +9,7 @@
<form action="." method="POST" accept-charset="utf-8">
<table border="0">
{{form.as_table()}}
-<tr><td/><td><input type="submit" class="submit" value="Save"></td></tr>
+<tr><td/><td><input type="submit" value="Save"></td></tr>
</table>
</form>
{% endblock %}
diff --git a/askbot/templates/user_profile/user.html b/askbot/templates/user_profile/user.html
index 8164784d..1f939d11 100644
--- a/askbot/templates/user_profile/user.html
+++ b/askbot/templates/user_profile/user.html
@@ -23,7 +23,6 @@
var viewUserID = {{view_user.id}};
askbot['data']['viewUserName'] = '{{ view_user.username|escape }}';
askbot['data']['viewUserId'] = {{ view_user.id }};
- askbot['data']['userPostsPageSize'] = {{ page_size }};
askbot['urls']['edit_group_membership'] = '{% url edit_group_membership %}';
askbot['urls']['getGroupsList'] = '{% url get_groups_list %}';
askbot['urls']['getTopAnswers'] = '{% url get_top_answers %}';
diff --git a/askbot/templates/user_profile/user_edit.html b/askbot/templates/user_profile/user_edit.html
index c95bf815..fa9ecfdd 100644
--- a/askbot/templates/user_profile/user_edit.html
+++ b/askbot/templates/user_profile/user_edit.html
@@ -106,8 +106,8 @@
{% endif %}
</table>
<div style="margin:30px 0 60px 0">
- <input type="submit" value="{% trans %}Update{% endtrans %}" class="submit" >&nbsp;
- <input id="cancel" type="button" value="{% trans %}Cancel{% endtrans %}" class="submit" >
+ <input type="submit" value="{% trans %}Update{% endtrans %}"/>
+ <input id="cancel" type="button" value="{% trans %}Cancel{% endtrans %}"/>
</div>
</div>
</form>
diff --git a/askbot/templates/user_profile/user_email_subscriptions.html b/askbot/templates/user_profile/user_email_subscriptions.html
index fdda03b7..100ce789 100644
--- a/askbot/templates/user_profile/user_email_subscriptions.html
+++ b/askbot/templates/user_profile/user_email_subscriptions.html
@@ -20,8 +20,8 @@
{{tag_filter_selection_form}}
</table>
<div class="submit-row text-align-right">
- <input type="submit" class="submit" name="save" value="{% trans %}Update{% endtrans %}"/>&nbsp;
- <input type="submit" class="submit" name="stop_email" value="{% trans %}Stop Email{% endtrans %}"/>
+ <input type="submit" name="save" value="{% trans %}Update{% endtrans %}"/>&nbsp;
+ <input type="submit" name="stop_email" value="{% trans %}Stop Email{% endtrans %}"/>
</div>
</form>
</div>
@@ -53,7 +53,7 @@
css_class = 'subscribed marked-tags special',
)
}}
- <br/>
+ <div class="clearfix"></div>
<div class="inputs">
<input id="subscribedTagInput" autocomplete="off" type="text"/>
<input id="subscribedTagAdd" type="submit" value="{% trans %}add{% endtrans%}"/>
@@ -61,14 +61,15 @@
</div>
{%endif%}
{% endblock %}
-{%block userjs%}
- <script type='text/javascript'>
- search = new FullTextSearch();
- askbot['controllers'] = askbot['controllers'] || {}
- askbot['controllers']['fullTextSearch'] = search;
- askbot['urls']['mark_subscribed_tag'] = '{% url mark_subscribed_tag %}';
- askbot['urls']['unmark_tag'] = '{% url unmark_tag %}';
- </script>
- <script type='text/javascript' src='{{"/js/tag_selector.js"|media}}'></script>
+{% block userjs %}
+ <script type='text/javascript'>
+ search = new FullTextSearch();
+ askbot['controllers'] = askbot['controllers'] || {}
+ askbot['controllers']['fullTextSearch'] = search;
+ askbot['urls']['mark_subscribed_tag'] = '{% url mark_subscribed_tag %}';
+ askbot['urls']['unmark_tag'] = '{% url unmark_tag %}';
+ askbot['settings']['tag_editor'] = '{{ tag_editor_settings|escapejs }}';
+ </script>
+ <script type='text/javascript' src='{{"/js/tag_selector.js"|media}}'></script>
{% endblock %}
<!-- end user_email_subscriptions.html -->
diff --git a/askbot/templates/user_profile/user_info.html b/askbot/templates/user_profile/user_info.html
index 93f1660f..6a9ff91e 100644
--- a/askbot/templates/user_profile/user_info.html
+++ b/askbot/templates/user_profile/user_info.html
@@ -125,7 +125,7 @@
</div>
<div class="col3 user-about">
{% if view_user.about and (not view_user.is_blocked()) %}
- {{view_user.about|linebreaks}}
+ {{view_user.about|linebreaks|escape}}
{% endif %}
</div>
</div>
diff --git a/askbot/templates/user_profile/user_moderate.html b/askbot/templates/user_profile/user_moderate.html
index a7f05b1c..75c6d6fe 100644
--- a/askbot/templates/user_profile/user_moderate.html
+++ b/askbot/templates/user_profile/user_moderate.html
@@ -17,7 +17,7 @@
</table>
<p id="id_user_status_info">
</p>
- <input type="submit" class="submit" name="change_status" value="{% trans %}Save{% endtrans %}" />
+ <input type="submit" name="change_status" value="{% trans %}Save{% endtrans %}" />
</form>
{% endif %}
<h3>
@@ -35,8 +35,8 @@
<table class="form-as-table">
{{ change_user_reputation_form.as_table() }}
</table>
- <input type="submit" class="submit" name="subtract_reputation" value="{% trans %}Subtract{% endtrans %}" />&nbsp;
- <input type="submit" class="submit" name="add_reputation" value="{% trans %}Add{% endtrans %}" />
+ <input type="submit" name="subtract_reputation" value="{% trans %}Subtract{% endtrans %}" />&nbsp;
+ <input type="submit" name="add_reputation" value="{% trans %}Add{% endtrans %}" />
</form>
{% if request.user != view_user %}
<hr/>
@@ -61,7 +61,7 @@
{% endif %}
{{ send_message_form.body_text}}
</div>
- <input type="submit" class="submit" name="send_message" value="{% trans %}Send message{% endtrans %}" />
+ <input type="submit" name="send_message" value="{% trans %}Send message{% endtrans %}" />
</form>
{% endif %}
{% endblock %}
diff --git a/askbot/templates/user_profile/user_stats.html b/askbot/templates/user_profile/user_stats.html
index 1ed9014a..344051af 100644
--- a/askbot/templates/user_profile/user_stats.html
+++ b/askbot/templates/user_profile/user_stats.html
@@ -85,7 +85,6 @@
{% if award.content_object and award.content_object_is_post %}
<li>
<a
- title="{{ award.content_object.get_snippet()|collapse|escape }}"
href="{{ award.content_object.get_absolute_url() }}"
>{% if award.content_type.post_type == 'answer' %}{% trans %}Answer to:{% endtrans %}{% endif %} {{ award.content_object.thread.title|escape }}</a>
</li>
@@ -102,6 +101,7 @@
{% block endjs %}
{{ super() }}
<script type="text/javascript">
+ askbot['data']['userPostsPageSize'] = {{ page_size }};
askbot['urls']['join_or_leave_group'] = '{% url join_or_leave_group %}';
$(document).ready(function(){
setup_badge_details_toggle();
diff --git a/askbot/templates/widgets/ask_form.html b/askbot/templates/widgets/ask_form.html
index df37ba3c..dec52d79 100644
--- a/askbot/templates/widgets/ask_form.html
+++ b/askbot/templates/widgets/ask_form.html
@@ -61,10 +61,14 @@
</div>
{% endif %}
</div>
- {% if not request.user.is_authenticated() %}
- <input type="submit" name="post_anon" value="{% trans %}Login/Signup to Post{% endtrans %}" class="submit" />
+ {% if form.recaptcha %}
+ <div>{{ macros.form_field_with_errors(form.recaptcha) }}</div>
+ <div class="clearfix"></div>
+ {% endif %}
+ {% if request.user.is_anonymous() and not settings.ALLOW_ASK_UNREGISTERED %}
+ <input type="submit" name="post_anon" value="{% trans %}Login/Signup to Post{% endtrans %}" />
{% else %}
- <input type="submit" name="post" value="{{ settings.WORDS_ASK_YOUR_QUESTION|escape }}" class="submit" />
+ <input type="submit" name="post" value="{{ settings.WORDS_ASK_YOUR_QUESTION|escape }}" />
{% endif %}
<div class="clean"></div>
</form>
diff --git a/askbot/templates/widgets/logo.html b/askbot/templates/widgets/logo.html
index 1b251432..c0349d1f 100644
--- a/askbot/templates/widgets/logo.html
+++ b/askbot/templates/widgets/logo.html
@@ -1,5 +1,7 @@
-<a id="logo" href="{% url questions %}"><img
- src="{{ settings.SITE_LOGO_URL|media }}"
- title="{% trans %}back to home page{% endtrans %}"
+<a
+ id="logo"
+ href="{% if settings.LOGO_DESTINATION_URL %}{{ settings.LOGO_DESTINATION_URL }}{% else %}{% url questions %}{% endif %}"
+><img
+ src="{{ settings.SITE_LOGO_URL|media }}"
alt="{% trans site=settings.APP_SHORT_NAME %}{{site}} logo{% endtrans %}"/>
</a>
diff --git a/askbot/templates/widgets/question_edit_tips.html b/askbot/templates/widgets/question_edit_tips.html
index 636bd38d..a6ac83b0 100644
--- a/askbot/templates/widgets/question_edit_tips.html
+++ b/askbot/templates/widgets/question_edit_tips.html
@@ -3,7 +3,7 @@
{{ settings.QUESTION_INSTRUCTIONS|safe }}
{% else %}
<ul>
- {% if not request.user.is_authenticated() %}
+ {% if request.user.is_anonymous() and not settings.ALLOW_ASK_UNREGISTERED %}
<li class="warning">{% trans %}since you are not logged in right now, you will be asked to sign in or register after making your post{% endtrans %}</li>
{% else %}
{% if settings.EMAIL_VALIDATION %}
diff --git a/askbot/templates/widgets/question_summary.html b/askbot/templates/widgets/question_summary.html
index 0ffaff96..62c97a53 100644
--- a/askbot/templates/widgets/question_summary.html
+++ b/askbot/templates/widgets/question_summary.html
@@ -44,15 +44,16 @@
<div style="clear:both"></div>
<div class="userinfo">
{{ timeago(thread.last_activity_at) }}
- {% if question.is_anonymous %}
+ {% if question.is_anonymous %}{# todo: here we need to look at the anonymity of the last edit instead #}
<span class="anonymous">{{ thread.last_activity_by.get_anonymous_name() }}</span>
{% else %}
<a href="{% url user_profile thread.last_activity_by.id, thread.last_activity_by.username|slugify %}">{{thread.last_activity_by.username|escape}}</a> {{ user_country_flag(thread.last_activity_by) }}
+ {% if thread.last_activity_by.get_primary_group() %}
+ - {{ user_primary_group(thread.last_activity_by) }}
+ {% endif %}
{% endif %}
- {% if thread.last_activity_by.get_primary_group() %}-{% endif %}
- {{ user_primary_group(thread.last_activity_by) }}
</div>
</div>
- <h2><a href="{{ question.get_absolute_url(thread=thread) }}">{{thread.get_title(question)|escape}}</a></h2>
+ <h2><a href="{{ question.get_absolute_url(thread=thread) }}">{{thread.get_title()|escape}}</a></h2>
{{ tag_list_widget(thread.get_tag_names(), search_state=search_state) }}
</div>
diff --git a/askbot/templatetags/extra_tags.py b/askbot/templatetags/extra_tags.py
index a74438de..68fea7e4 100644
--- a/askbot/templatetags/extra_tags.py
+++ b/askbot/templatetags/extra_tags.py
@@ -15,7 +15,7 @@ GRAVATAR_TEMPLATE = (
'<a style="text-decoration:none" '
'href="%(user_profile_url)s"><img class="gravatar" '
'width="%(size)s" height="%(size)s" '
- 'src="//www.gravatar.com/avatar/%(gravatar_hash)s'
+ 'src="%(gravatar_url)s/%(gravatar_hash)s'
'?s=%(size)s&amp;d=%(gravatar_type)s&amp;r=PG" '
'title="%(username)s" '
'alt="%(alt_text)s" /></a>')
@@ -37,6 +37,7 @@ def gravatar(user, size):
)
#safe_username = template.defaultfilters.urlencode(username)
return mark_safe(GRAVATAR_TEMPLATE % {
+ 'gravatar_url': askbot_settings.GRAVATAR_BASE_URL,
'user_profile_url': user_profile_url,
'size': size,
'gravatar_hash': functions.get_from_dict_or_object(user, 'gravatar'),
diff --git a/askbot/tests/user_model_tests.py b/askbot/tests/user_model_tests.py
index b11fb151..786991a0 100644
--- a/askbot/tests/user_model_tests.py
+++ b/askbot/tests/user_model_tests.py
@@ -31,7 +31,8 @@ class UserModelTests(AskbotTestCase):
bulk_subscription = models.BulkTagSubscription.objects.create(
tag_names=[one_tag.name, another_tag.name],
group_list=[global_group],
- tag_author=the_boss
+ tag_author=the_boss,
+ language_code='en'
)
user = self.create_user('someone')
diff --git a/askbot/tests/utils.py b/askbot/tests/utils.py
index 0a207464..bb9a6103 100644
--- a/askbot/tests/utils.py
+++ b/askbot/tests/utils.py
@@ -290,7 +290,7 @@ class AskbotTestCase(TestCase):
except models.User.DoesNotExist:
user = self.create_user('tag_creator')
- tag = models.Tag(created_by = user, name = tag_name)
+ tag = models.Tag(created_by=user, name=tag_name, language_code='en')
tag.save()
return tag
diff --git a/askbot/urls.py b/askbot/urls.py
index e00a00e0..799ef0f5 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -4,7 +4,14 @@ askbot askbot url configuraion file
import os.path
import django
from django.conf import settings
-from django.conf.urls.defaults import url, patterns, include
+
+try:
+ from django.conf.urls import url, patterns, include
+ from django.conf.urls import handler404
+except ImportError:
+ from django.conf.urls.defaults import url, patterns, include
+ from django.conf.urls.defaults import handler404
+
from django.contrib import admin
from askbot import views
from askbot.feed import RssLastestQuestionsFeed, RssIndividualQuestionFeed
@@ -118,11 +125,6 @@ urlpatterns = patterns('',
name='groups'
),
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 ef318580..b955168c 100644
--- a/askbot/utils/console.py
+++ b/askbot/utils/console.py
@@ -25,7 +25,7 @@ def choice_dialog(prompt_phrase, choices = None, invalid_phrase = None):
assert(not isinstance(choices, basestring))
while 1:
response = raw_input(
- '\n%s (type %s)\n> ' % (prompt_phrase, '/'.join(choices))
+ '\n%s\ntype %s: ' % (prompt_phrase, '/'.join(choices))
)
if response in choices:
return response
diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py
index 1cf20059..815e6e2d 100644
--- a/askbot/utils/decorators.py
+++ b/askbot/utils/decorators.py
@@ -42,7 +42,7 @@ def ajax_login_required(view_func):
return view_func(request, *args, **kwargs)
else:
json = simplejson.dumps({'login_required':True})
- return HttpResponseForbidden(json, mimetype='application/json')
+ return HttpResponseForbidden(json, content_type='application/json')
return wrap
@@ -107,7 +107,7 @@ def ajax_only(view_func):
'message': message,
'success': 0
}
- return HttpResponse(simplejson.dumps(data), mimetype='application/json')
+ return HttpResponse(simplejson.dumps(data), content_type='application/json')
if isinstance(data, HttpResponse):#is this used?
data.mimetype = 'application/json'
@@ -115,7 +115,7 @@ def ajax_only(view_func):
else:
data['success'] = 1
json = simplejson.dumps(data)
- return HttpResponse(json, mimetype='application/json')
+ return HttpResponse(json, content_type='application/json')
return wrapper
def check_authorization_to_post(func_or_message):
diff --git a/askbot/utils/html.py b/askbot/utils/html.py
index a9b489f4..3aa793f4 100644
--- a/askbot/utils/html.py
+++ b/askbot/utils/html.py
@@ -75,7 +75,7 @@ def format_url_replacement(url, text):
return '%s (%s)' % (url, text)
return url or text or ''
-def urlize_html(html):
+def urlize_html(html, trim_url_limit=40):
"""will urlize html, while ignoring link
patterns inside anchors, <pre> and <code> tags
"""
@@ -89,7 +89,7 @@ def urlize_html(html):
#bs4 is weird, so we work around to replace nodes
#maybe there is a better way though
- urlized_text = urlize(node, trim_url_limit=40)
+ urlized_text = urlize(node, trim_url_limit=trim_url_limit)
if unicode(node) == urlized_text:
continue
diff --git a/askbot/utils/pluralization.py b/askbot/utils/pluralization.py
index 74549f10..8d78fa77 100644
--- a/askbot/utils/pluralization.py
+++ b/askbot/utils/pluralization.py
@@ -89,7 +89,7 @@ FORMULAE = {
}
GERMANNIC_FAMILY = (
- 'en', 'bg', 'bg_BG', 'el', 'nb_NO', 'pt', 'ast', 'ca', 'de',
+ 'en', 'bg', 'bg_BG', 'el', 'nb_NO', 'pt', 'ast', 'ca', 'de',
'it', 'hu', 'hi', 'sv_SE', 'fi', 'he_IL', 'gl', 'es', 'bn_IN'
)
@@ -105,7 +105,7 @@ SLOVENIAN_FAMILY = ('sl',)
CHECH_FAMILY = ('cs', 'cs_CZ')
SINGULAR_FAMILY = (
- 'zh_HK', 'fa_IR', 'zh_CN', 'id_ID', 'zh_TW', 'ko', 'ms_MY', 'tr', 'tr_TR', 'vi', 'ja'
+ 'zh_HK', 'fa_IR', 'zh_CN', 'id_ID', 'zh_TW', 'ko', 'ms_MY', 'tr', 'tr_TR', 'vi', 'ja', 'uz', 'uz_UZ'
)
def get_formula(lang):
diff --git a/askbot/utils/translation.py b/askbot/utils/translation.py
index 7e1231d3..7be7585b 100644
--- a/askbot/utils/translation.py
+++ b/askbot/utils/translation.py
@@ -1,3 +1,6 @@
+"""todo: move here all functions related to languages
+in other utils modules
+"""
from django.conf import settings as django_settings
from askbot.conf import settings as askbot_settings
from django.utils import translation
diff --git a/askbot/utils/url_utils.py b/askbot/utils/url_utils.py
index 3c4fa7b5..a656cde4 100644
--- a/askbot/utils/url_utils.py
+++ b/askbot/utils/url_utils.py
@@ -2,7 +2,10 @@ import os
import urlparse
from django.core.urlresolvers import reverse
from django.conf import settings
-from django.conf.urls.defaults import url
+try:
+ from django.conf.urls import url
+except ImportError:
+ from django.conf.urls.defaults import url
def service_url(*args, **kwargs):
"""adds the service prefix to the url"""
diff --git a/askbot/views/api_v1.py b/askbot/views/api_v1.py
index d55c8462..c8cab137 100644
--- a/askbot/views/api_v1.py
+++ b/askbot/views/api_v1.py
@@ -64,7 +64,7 @@ def info(request):
data['groups'] = 0
json_string = simplejson.dumps(data)
- return HttpResponse(json_string, mimetype='application/json')
+ return HttpResponse(json_string, content_type='application/json')
def user(request, user_id):
'''
@@ -76,7 +76,7 @@ def user(request, user_id):
data['answers'] = models.Post.objects.get_answers(user).count()
data['comments'] = models.Post.objects.filter(post_type='comment').count()
json_string = simplejson.dumps(data)
- return HttpResponse(json_string, mimetype='application/json')
+ return HttpResponse(json_string, content_type='application/json')
def users(request):
@@ -126,7 +126,7 @@ def users(request):
}
json_string = simplejson.dumps(response_dict)
- return HttpResponse(json_string, mimetype='application/json')
+ return HttpResponse(json_string, content_type='application/json')
def question(request, question_id):
@@ -141,7 +141,7 @@ def question(request, question_id):
)
datum = get_question_data(post.thread)
json_string = simplejson.dumps(datum)
- return HttpResponse(json_string, mimetype='application/json')
+ return HttpResponse(json_string, content_type='application/json')
def questions(request):
@@ -196,4 +196,4 @@ def questions(request):
'questions': question_list
}
response_data = simplejson.dumps(ajax_data)
- return HttpResponse(response_data, mimetype='application/json')
+ return HttpResponse(response_data, content_type='application/json')
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index 03f16ad0..2ccad9d5 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -25,6 +25,7 @@ from django.template.loader import get_template
from django.views.decorators import csrf
from django.utils import simplejson
from django.utils.html import escape
+from django.utils import translation
from django.utils.translation import ugettext as _
from django.utils.translation import string_concat
from askbot.utils.slug import slugify
@@ -130,7 +131,7 @@ def manage_inbox(request):
response_data['success'] = True
data = simplejson.dumps(response_data)
- return HttpResponse(data, mimetype="application/json")
+ return HttpResponse(data, content_type="application/json")
else:
raise exceptions.PermissionDenied(
_('Sorry, but anonymous users cannot access the inbox')
@@ -147,7 +148,7 @@ def manage_inbox(request):
response_data['message'] = message
response_data['success'] = False
data = simplejson.dumps(response_data)
- return HttpResponse(data, mimetype="application/json")
+ return HttpResponse(data, content_type="application/json")
def process_vote(user = None, vote_direction = None, post = None):
@@ -434,7 +435,7 @@ def vote(request):
response_data['message'] = unicode(e)
response_data['success'] = 0
data = simplejson.dumps(response_data)
- return HttpResponse(data, mimetype="application/json")
+ return HttpResponse(data, content_type="application/json")
#internally grouped views - used by the tagging system
@csrf.csrf_exempt
@@ -472,12 +473,13 @@ def mark_tag(request, **kwargs):#tagging system
for name in wildcards:
if name in cleaned_wildcards:
tag_usage_counts[name] = models.Tag.objects.filter(
- name__startswith = name[:-1]
+ name__startswith = name[:-1],
+ language_code=translation.get_language()
).count()
else:
tag_usage_counts[name] = 0
- return HttpResponse(simplejson.dumps(tag_usage_counts), mimetype="application/json")
+ return HttpResponse(simplejson.dumps(tag_usage_counts), content_type="application/json")
#@decorators.ajax_only
@decorators.get_only
@@ -511,7 +513,7 @@ def get_thread_shared_users(request):
'users_count': users.count(),
'success': True
})
- return HttpResponse(re_data, mimetype='application/json')
+ return HttpResponse(re_data, content_type='application/json')
@decorators.get_only
def get_thread_shared_groups(request):
@@ -527,7 +529,7 @@ def get_thread_shared_groups(request):
'groups_count': groups.count(),
'success': True
})
- return HttpResponse(re_data, mimetype='application/json')
+ return HttpResponse(re_data, content_type='application/json')
@decorators.ajax_only
def get_html_template(request):
@@ -549,8 +551,9 @@ def get_tag_list(request):
function
"""
tags = models.Tag.objects.filter(
- deleted = False,
- status = models.Tag.STATUS_ACCEPTED
+ deleted=False,
+ status=models.Tag.STATUS_ACCEPTED,
+ language_code=translation.get_language()
)
tag_names = tags.values_list(
@@ -737,12 +740,14 @@ def create_bulk_tag_subscription(request):
tag_names = form.cleaned_data['tags'].split(' ')
user_list = form.cleaned_data.get('users')
group_list = form.cleaned_data.get('groups')
+ lang = translation.get_language()
bulk_subscription = models.BulkTagSubscription.objects.create(
tag_names=tag_names,
tag_author=request.user,
user_list=user_list,
- group_list=group_list
+ group_list=group_list,
+ language_code=lang
)
return HttpResponseRedirect(reverse('list_bulk_tag_subscription'))
@@ -772,12 +777,20 @@ def edit_bulk_tag_subscription(request, pk):
group_ids = [user.id for user in form.cleaned_data['groups']]
bulk_subscription.groups.add(*group_ids)
- tags, new_tag_names = get_tags_by_names(form.cleaned_data['tags'].split(' '))
+ lang = translation.get_language()
+
+ tags, new_tag_names = get_tags_by_names(
+ form.cleaned_data['tags'].split(' '),
+ language_code=lang
+ )
tag_id_list = [tag.id for tag in tags]
for new_tag_name in new_tag_names:
- new_tag = models.Tag.objects.create(name=new_tag_name,
- created_by=request.user)
+ new_tag = models.Tag.objects.create(
+ name=new_tag_name,
+ created_by=request.user,
+ language_code=lang
+ )
tag_id_list.append(new_tag.id)
bulk_subscription.tags.add(*tag_id_list)
@@ -1002,7 +1015,7 @@ def delete_post(request):
@csrf.csrf_exempt
def read_message(request):#marks message a read
if request.method == "POST":
- if request.POST['formdata'] == 'required':
+ if request.POST.get('formdata') == 'required':
request.session['message_silent'] = 1
if request.user.is_authenticated():
request.user.delete_messages()
@@ -1250,13 +1263,21 @@ def moderate_suggested_tag(request):
tag_id = form.cleaned_data['tag_id']
thread_id = form.cleaned_data.get('thread_id', None)
+ lang = translation.get_language()
+
try:
- tag = models.Tag.objects.get(id=tag_id)#can tag not exist?
+ tag = models.Tag.objects.get(
+ id=tag_id,
+ language_code=lang
+ )#can tag not exist?
except models.Tag.DoesNotExist:
return
if thread_id:
- threads = models.Thread.objects.filter(id=thread_id)
+ threads = models.Thread.objects.filter(
+ id=thread_id,
+ language_code=lang
+ )
else:
threads = tag.threads.none()
@@ -1513,7 +1534,7 @@ def get_editor(request):
'scripts': parsed_scripts,
'success': True
}
- return HttpResponse(simplejson.dumps(data), mimetype='application/json')
+ return HttpResponse(simplejson.dumps(data), content_type='application/json')
@csrf.csrf_exempt
@decorators.ajax_only
diff --git a/askbot/views/error.py b/askbot/views/error.py
index fab922ee..827e2240 100644
--- a/askbot/views/error.py
+++ b/askbot/views/error.py
@@ -1,10 +1,16 @@
-from django.shortcuts import render
+from django.http import HttpResponseServerError
+from django.template import RequestContext
+from django.template.loader import get_template
def internal_error(request):
- data = {}
+ """Error 500 view with context"""
+ template = get_template('500.html')
try:
- from askbot.conf import settings as askbot_settings
- data['settings'] = askbot_settings
+ result = template.render(RequestContext(request))
except Exception:
- data['settings'] = {}
- return render(request, '500.html', data)
+ #if context loading fails, we try to get settings separately
+ from askbot.conf import settings as askbot_settings
+ data = {'settings': askbot_settings.as_dict()}
+ result = template.render(RequestContext(request, data))
+
+ return HttpResponseServerError(result)
diff --git a/askbot/views/meta.py b/askbot/views/meta.py
index 49e24a5e..a27f2971 100644
--- a/askbot/views/meta.py
+++ b/askbot/views/meta.py
@@ -14,6 +14,7 @@ from django.http import HttpResponse
from django.http import HttpResponseForbidden
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
+from django.utils import translation
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from django.views import static
@@ -24,7 +25,7 @@ from askbot.conf import settings as askbot_settings
from askbot.forms import FeedbackForm
from askbot.utils.url_utils import get_login_url
from askbot.utils.forms import get_next_url
-from askbot.mail import mail_moderators
+from askbot.mail import mail_moderators, send_mail
from askbot.models import BadgeData, Award, User, Tag
from askbot.models import badges as badge_data
from askbot.skins.loaders import render_text_into_skin
@@ -33,6 +34,8 @@ from askbot.utils.forms import get_next_url
from askbot.utils import functions
from recaptcha_works.decorators import fix_recaptcha_remote_ip
+import re
+
def generic_view(request, template = None, page_class = None):
"""this may be not necessary, since it is just a rewrite of render"""
if request is None: # a plug for strange import errors in django startup
@@ -104,10 +107,7 @@ def feedback(request):
return HttpResponseRedirect(redirect_url)
if request.method == "POST":
- form = FeedbackForm(
- is_auth=request.user.is_authenticated(),
- data=request.POST
- )
+ form = FeedbackForm(user=request.user, data=request.POST)
if form.is_valid():
if not request.user.is_authenticated():
@@ -123,18 +123,29 @@ def feedback(request):
headers = {}
if data['email']:
headers = {'Reply-To': data['email']}
-
- mail_moderators(
- _('Q&A forum feedback'),
- message,
- headers=headers
- )
+ subject = _('Q&A forum feedback')
+ if askbot_settings.FEEDBACK_EMAILS:
+ recipients = re.split('\s*,\s*', askbot_settings.FEEDBACK_EMAILS)
+ send_mail(
+ subject_line=subject,
+ body_text=message,
+ headers=headers,
+ recipient_list=recipients,
+ )
+ else:
+ mail_moderators(
+ subject_line=subject,
+ body_text=message,
+ headers=headers
+ )
msg = _('Thanks for the feedback!')
request.user.message_set.create(message=msg)
return HttpResponseRedirect(get_next_url(request))
else:
- form = FeedbackForm(is_auth = request.user.is_authenticated(),
- initial={'next':get_next_url(request)})
+ form = FeedbackForm(
+ user=request.user,
+ initial={'next':get_next_url(request)}
+ )
data['form'] = form
return render(request, 'feedback.html', data)
@@ -198,7 +209,10 @@ def list_suggested_tags(request):
or cancel the moderation reuest."""
if askbot_settings.ENABLE_TAG_MODERATION == False:
raise Http404
- tags = Tag.objects.filter(status = Tag.STATUS_SUGGESTED)
+ tags = Tag.objects.filter(
+ status = Tag.STATUS_SUGGESTED,
+ language_code=translation.get_language()
+ )
tags = tags.order_by('-used_count', 'name')
#paginate moderated tags
paginator = Paginator(tags, 20)
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 155be946..80f1d2ec 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -42,7 +42,6 @@ from askbot.forms import GetDataForPostForm
from askbot.utils.loading import load_module
from askbot import conf
from askbot import models
-from askbot import schedules
from askbot.models.tag import Tag
from askbot import const
from askbot.startup_procedures import domain_is_bad
@@ -252,7 +251,6 @@ def questions(request, **kwargs):
'font_size' : extra_tags.get_tag_font_size(related_tags),
'display_tag_filter_strategy_choices': conf.get_tag_display_filter_strategy_choices(),
'email_tag_filter_strategy_choices': conf.get_tag_email_filter_strategy_choices(),
- 'update_avatar_data': schedules.should_update_avatar_data(request),
'query_string': search_state.query_string(),
'search_state': search_state,
'feed_url': context_feed_url,
@@ -300,7 +298,7 @@ def get_top_answers(request):
'html': answers_html,
'num_answers': paginator.count}
)
- return HttpResponse(json_string, mimetype='application/json')
+ return HttpResponse(json_string, content_type='application/json')
else:
return HttpResponseBadRequest()
@@ -323,7 +321,10 @@ def tags(request):#view showing a listing of available tags - plain list
tag_list_type = askbot_settings.TAG_LIST_FORMAT
#2) Get query set for the tags.
- query_params = {'deleted': False}
+ query_params = {
+ 'deleted': False,
+ 'language_code': translation.get_language()
+ }
if query != '':
query_params['name__icontains'] = query
@@ -373,7 +374,7 @@ def tags(request):#view showing a listing of available tags - plain list
template_context = RequestContext(request, data)
json_data = {'success': True, 'html': template.render(template_context)}
json_string = simplejson.dumps(json_data)
- return HttpResponse(json_string, mimetype='application/json')
+ return HttpResponse(json_string, content_type='application/json')
else:
return render(request, 'tags.html', data)
diff --git a/askbot/views/users.py b/askbot/views/users.py
index 57397457..91f210d9 100644
--- a/askbot/views/users.py
+++ b/askbot/views/users.py
@@ -26,6 +26,7 @@ from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseForbidden
from django.http import HttpResponseRedirect, Http404
+from django.utils.translation import get_language
from django.utils.translation import ugettext as _
from django.utils import simplejson
from django.utils.html import strip_tags as strip_all_tags
@@ -441,7 +442,10 @@ def user_stats(request, user, context):
# INFO: There's bug in Django that makes the following query kind of broken (GROUP BY clause is problematic):
# http://stackoverflow.com/questions/7973461/django-aggregation-does-excessive-group-by-clauses
# Fortunately it looks like it returns correct results for the test data
- user_tags = models.Tag.objects.filter(threads__posts__author=user).distinct().\
+ user_tags = models.Tag.objects.filter(
+ threads__posts__author=user,
+ language_code=get_language()
+ ).distinct().\
annotate(user_tag_usage_count=Count('threads')).\
order_by('-user_tag_usage_count')[:const.USER_VIEW_DATA_SIZE]
user_tags = list(user_tags) # evaluate
@@ -994,6 +998,8 @@ def user_email_subscriptions(request, user, context):
'user_languages': user.languages.split()
}
context.update(data)
+ #todo: really need only if subscribed tags are enabled
+ context.update(view_context.get_for_tag_editor())
return render(
request,
'user_profile/user_email_subscriptions.html',
@@ -1090,17 +1096,6 @@ def user(request, id, slug=None, tab_name=None):
context['custom_tab_slug'] = CUSTOM_TAB['SLUG']
return user_view_func(request, profile_owner, context)
-@csrf.csrf_exempt
-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()
-
def groups(request, id = None, slug = None):
"""output groups page
"""
diff --git a/askbot/views/widgets.py b/askbot/views/widgets.py
index c049f91d..41cf4c8f 100644
--- a/askbot/views/widgets.py
+++ b/askbot/views/widgets.py
@@ -245,7 +245,7 @@ def render_ask_widget_js(request, widget_id):
'variable_name': variable_name
}
content = content_tpl.render(RequestContext(request, context_dict))
- return HttpResponse(content, mimetype='text/javascript')
+ return HttpResponse(content, content_type='text/javascript')
def render_ask_widget_css(request, widget_id):
widget = get_object_or_404(models.AskWidget, pk=widget_id)
@@ -258,7 +258,7 @@ def render_ask_widget_css(request, widget_id):
'variable_name': variable_name
}
content = content_tpl.render(RequestContext(request, context_dict))
- return HttpResponse(content, mimetype='text/css')
+ return HttpResponse(content, content_type='text/css')
def question_widget(request, widget_id):
"""Returns the first x questions based on certain tags.
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index 9234c37f..a11fb941 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -30,6 +30,7 @@ from django.core.urlresolvers import reverse
from django.core import exceptions
from django.conf import settings
from django.views.decorators import csrf
+from django.contrib.auth.models import User
from askbot import exceptions as askbot_exceptions
from askbot import forms
@@ -46,6 +47,7 @@ from askbot.views import context
from askbot.templatetags import extra_filters_jinja as template_filters
from askbot.importers.stackexchange import management as stackexchange#todo: may change
from askbot.utils.slug import slugify
+from recaptcha_works.decorators import fix_recaptcha_remote_ip
# used in index page
INDEX_PAGE_SIZE = 20
@@ -126,7 +128,7 @@ def upload(request):#ajax upload file to a question or answer
xml_template = "<result><msg><![CDATA[%s]]></msg><error><![CDATA[%s]]></error><file_url>%s</file_url><orig_file_name><![CDATA[%s]]></orig_file_name></result>"
xml = xml_template % (result, error, file_url, orig_file_name)
- return HttpResponse(xml, mimetype="application/xml")
+ return HttpResponse(xml, content_type="application/xml")
def __import_se_data(dump_file):
"""non-view function that imports the SE data
@@ -192,7 +194,7 @@ def import_data(request):
dump_storage.flush()
return HttpResponse(__import_se_data(dump_storage))
- #yield HttpResponse(_('StackExchange import complete.'), mimetype='text/plain')
+ #yield HttpResponse(_('StackExchange import complete.'), content_type='text/plain')
#dump_storage.close()
else:
form = forms.DumpUploadForm()
@@ -203,7 +205,7 @@ def import_data(request):
}
return render(request, 'import_data.html', data)
-#@login_required #actually you can post anonymously, but then must register
+@fix_recaptcha_remote_ip
@csrf.csrf_protect
@decorators.check_authorization_to_post(ugettext_lazy('Please log in to make posts'))
@decorators.check_spam('text')
@@ -223,8 +225,8 @@ def ask(request):#view used to ask a new question
if askbot_settings.READ_ONLY_MODE_ENABLED:
return HttpResponseRedirect(reverse('index'))
- form = forms.AskForm(request.REQUEST, user=request.user)
if request.method == 'POST':
+ form = forms.AskForm(request.POST, user=request.user)
if form.is_valid():
timestamp = datetime.datetime.now()
title = form.cleaned_data['title']
@@ -237,12 +239,16 @@ def ask(request):#view used to ask a new question
language = form.cleaned_data.get('language', None)
if request.user.is_authenticated():
- drafts = models.DraftQuestion.objects.filter(
- author=request.user
- )
+ drafts = models.DraftQuestion.objects.filter(author=request.user)
drafts.delete()
-
user = form.get_post_user(request.user)
+ elif request.user.is_anonymous() and askbot_settings.ALLOW_ASK_UNREGISTERED:
+ user = models.get_or_create_anonymous_user()
+ ask_anonymously = True
+ else:
+ user = None
+
+ if user:
try:
question = user.post_question(
title=title,
@@ -349,7 +355,7 @@ def retag_question(request, id):
response_data['message'] = message
data = simplejson.dumps(response_data)
- return HttpResponse(data, mimetype="application/json")
+ return HttpResponse(data, content_type="application/json")
else:
return HttpResponseRedirect(question.get_absolute_url())
elif request.is_ajax():
@@ -358,7 +364,7 @@ def retag_question(request, id):
'success': False
}
data = simplejson.dumps(response_data)
- return HttpResponse(data, mimetype="application/json")
+ return HttpResponse(data, content_type="application/json")
else:
form = forms.RetagQuestionForm(question)
@@ -375,7 +381,7 @@ def retag_question(request, id):
'success': False
}
data = simplejson.dumps(response_data)
- return HttpResponse(data, mimetype="application/json")
+ return HttpResponse(data, content_type="application/json")
else:
request.user.message_set.create(message = unicode(e))
return HttpResponseRedirect(question.get_absolute_url())
@@ -383,6 +389,7 @@ def retag_question(request, id):
@login_required
@csrf.csrf_protect
@decorators.check_spam('text')
+@fix_recaptcha_remote_ip
def edit_question(request, id):
"""edit question view
"""
@@ -434,9 +441,6 @@ def edit_question(request, id):
if form.cleaned_data['reveal_identity']:
question.thread.remove_author_anonymity()
- if 'language' in form.cleaned_data:
- question.thread.language_code = form.cleaned_data['language']
-
is_anon_edit = form.cleaned_data['stay_anonymous']
is_wiki = form.cleaned_data.get('wiki', question.wiki)
post_privately = form.cleaned_data['post_privately']
@@ -455,6 +459,10 @@ def edit_question(request, id):
is_private = post_privately,
suppress_email=suppress_email
)
+
+ if 'language' in form.cleaned_data:
+ question.thread.set_language_code(form.cleaned_data['language'])
+
return HttpResponseRedirect(question.get_absolute_url())
else:
#request type was "GET"
@@ -492,6 +500,7 @@ def edit_question(request, id):
@login_required
@csrf.csrf_protect
@decorators.check_spam('text')
+@fix_recaptcha_remote_ip
def edit_answer(request, id):
answer = get_object_or_404(models.Post, id=id)
@@ -587,6 +596,7 @@ def edit_answer(request, id):
#todo: rename this function to post_new_answer
@decorators.check_authorization_to_post(ugettext_lazy('Please log in to make posts'))
@decorators.check_spam('text')
+@fix_recaptcha_remote_ip
def answer(request, id, form_class=forms.AnswerForm):#process a new answer
"""view that posts new answer
@@ -691,7 +701,7 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po
json_comments.append(comment_data)
data = simplejson.dumps(json_comments)
- return HttpResponse(data, mimetype="application/json")
+ return HttpResponse(data, content_type="application/json")
@csrf.csrf_exempt
@decorators.check_spam('comment')
@@ -751,7 +761,7 @@ def post_comments(request):#generic ajax handler to load comments to an object
)
response = __generate_comments_json(post, user)
except exceptions.PermissionDenied, e:
- response = HttpResponseForbidden(unicode(e), mimetype="application/json")
+ response = HttpResponseForbidden(unicode(e), content_type="application/json")
return response
@@ -819,7 +829,7 @@ def delete_comment(request):
raise exceptions.PermissionDenied(msg)
if request.is_ajax():
- form = forms.DeleteCommentForm(request.POST)
+ form = forms.ProcessCommentForm(request.POST)
if form.is_valid() == False:
return HttpResponseBadRequest()
@@ -849,19 +859,24 @@ def delete_comment(request):
mimetype = 'application/json'
)
+@login_required
@decorators.post_only
+@csrf.csrf_protect
def comment_to_answer(request):
+ if request.user.is_anonymous():
+ msg = _('Sorry, only logged in users can convert comments to answers. '
+ 'Please <a href="%(sign_in_url)s">sign in</a>.') % \
+ {'sign_in_url': url_utils.get_login_url()}
+ raise exceptions.PermissionDenied(msg)
- try:
- comment_id = int(request.POST.get('comment_id'))
- except (ValueError, TypeError):
- #type or value error is raised is int() fails
+ form = forms.ProcessCommentForm(request.POST)
+ if form.is_valid() == False:
raise Http404
comment = get_object_or_404(
models.Post,
post_type='comment',
- id=comment_id
+ id=form.cleaned_data['comment_id']
)
if askbot_settings.READ_ONLY_MODE_ENABLED is False:
@@ -879,14 +894,23 @@ def repost_answer_as_comment(request, destination=None):
'comment_under_previous_answer'
)
)
+ if request.user.is_anonymous():
+ msg = _('Sorry, only logged in users can convert answers to comments. '
+ 'Please <a href="%(sign_in_url)s">sign in</a>.') % \
+ {'sign_in_url': url_utils.get_login_url()}
+ raise exceptions.PermissionDenied(msg)
answer_id = request.POST.get('answer_id')
if answer_id:
- answer_id = int(answer_id)
+ try:
+ answer_id = int(answer_id)
+ except (ValueError, TypeError):
+ raise Http404
answer = get_object_or_404(models.Post,
post_type = 'answer', id=answer_id)
if askbot_settings.READ_ONLY_MODE_ENABLED:
return HttpResponseRedirect(answer.get_absolute_url())
+ request.user.assert_can_convert_post(post=answer)
if destination == 'comment_under_question':
destination_post = answer.thread._question_post()
diff --git a/askbot_requirements.txt b/askbot_requirements.txt
index 59694444..3b9fde6e 100644
--- a/askbot_requirements.txt
+++ b/askbot_requirements.txt
@@ -1,5 +1,5 @@
akismet
-django>=1.3.1
+django>=1.3.1,<1.6
Jinja2
Coffin>=0.3
South>=0.7.1
@@ -12,7 +12,7 @@ django-threaded-multihost
django-robots
unidecode
django-countries==1.0.5
-django-celery==2.2.7
+django-celery>=3.0.11
django-kombu==0.9.2
django-followit
django-recaptcha-works
diff --git a/askbot_requirements_dev.txt b/askbot_requirements_dev.txt
index e5c36577..476ed66f 100644
--- a/askbot_requirements_dev.txt
+++ b/askbot_requirements_dev.txt
@@ -1,5 +1,5 @@
akismet
-django==1.4.2
+django>=1.3.1,<1.6
Jinja2
Coffin>=0.3
South>=0.7.1
@@ -15,7 +15,7 @@ django-threaded-multihost
django-robots
unidecode
django-countries==1.0.5
-django-celery==2.2.7
+django-celery>=3.0.11
django-kombu==0.9.2
django-followit
django-recaptcha-works