diff options
134 files changed, 4431 insertions, 701 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/access_control.py b/askbot/conf/access_control.py index 0e3dcb54..c339da6d 100644 --- a/askbot/conf/access_control.py +++ b/askbot/conf/access_control.py @@ -1,6 +1,8 @@ from askbot.conf.settings_wrapper import settings from askbot.conf.super_groups import LOGIN_USERS_COMMUNICATION from askbot.deps import livesettings +from askbot.deps.livesettings import BooleanValue +from askbot.deps.livesettings import StringValue from django.utils.translation import ugettext_lazy as _ ACCESS_CONTROL = livesettings.ConfigurationGroup( @@ -10,6 +12,26 @@ ACCESS_CONTROL = livesettings.ConfigurationGroup( ) settings.register( + BooleanValue( + ACCESS_CONTROL, + 'READ_ONLY_MODE_ENABLED', + default=False, + description=_('Make site read-only'), + ) +) + +settings.register( + StringValue( + ACCESS_CONTROL, + 'READ_ONLY_MESSAGE', + default=_( + 'The site is temporarily read-only. ' + 'Only viewing of the content is possible at the moment.' + ) + ) +) + +settings.register( livesettings.BooleanValue( ACCESS_CONTROL, 'ASKBOT_CLOSED_FORUM_MODE', 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_modes.py b/askbot/conf/site_modes.py index 98c35505..29e537db 100644 --- a/askbot/conf/site_modes.py +++ b/askbot/conf/site_modes.py @@ -6,7 +6,8 @@ Site modes settings: """ from askbot.conf.settings_wrapper import settings from askbot.conf.super_groups import REP_AND_BADGES -from askbot.deps.livesettings import ConfigurationGroup, BooleanValue +from askbot.deps.livesettings import ConfigurationGroup +from askbot.deps.livesettings import BooleanValue from django.utils.translation import ugettext_lazy as _ LARGE_SITE_MODE_SETTINGS = { 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/user_settings.py b/askbot/conf/user_settings.py index a2d8d386..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', @@ -37,8 +38,17 @@ settings.register( livesettings.BooleanValue( USER_SETTINGS, 'EDITABLE_SCREEN_NAME', - default = True, - description = _('Allow editing user screen name') + default=True, + description=_('Allow editing user screen name') + ) +) + +settings.register( + livesettings.BooleanValue( + USER_SETTINGS, + 'SHOW_ADMINS_PRIVATE_USER_DATA', + default=False, + description=_('Show email addresses to moderators') ) ) @@ -112,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, @@ -124,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 7f0229d7..e554e324 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -3,11 +3,17 @@ Changes in Askbot Development master branch (only on github) ------------------------------------------ - +* 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. * Management command to add data from other Askbot site. * Allowed simple overrides of livesettings with `ASKBOT_...` prefixed - variables in the `settings.py` file + variables in the `settings.py` file. 0.7.49 (Sep 19, 2013) --------------------- diff --git a/askbot/doc/source/contributors.rst b/askbot/doc/source/contributors.rst index 0dfb942b..2ebcaebb 100644 --- a/askbot/doc/source/contributors.rst +++ b/askbot/doc/source/contributors.rst @@ -54,6 +54,10 @@ 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>`_ 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 c0a30679..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,24 +931,17 @@ class AskForm(PostAsSomeoneForm, PostPrivatelyForm): self.fields['ask_anonymously'] = forms.BooleanField( label=_('post anonymously'), - required=False, + required=False ) - if (not user.is_authenticated() - and askbot_settings.USE_RECAPTCHA - and askbot_settings.ALLOW_ASK_ANONYMOUSLY): - self.fields['recaptcha'] = RecaptchaField( - private_key=askbot_settings.RECAPTCHA_SECRET, - public_key=askbot_settings.RECAPTCHA_KEY, - label='Are you human?', required=True) + if user.is_anonymous() or not askbot_settings.ALLOW_ASK_ANONYMOUSLY: + self.hide_field('ask_anonymously') - #hide ask_anonymously field if getattr(django_settings, 'ASKBOT_MULTILINGUAL', False): self.fields['language'] = LanguageField() - if ( not askbot_settings.ALLOW_ASK_ANONYMOUSLY - or not user.is_authenticated()): - 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 @@ -965,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: @@ -974,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) @@ -1117,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""" @@ -1233,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 @@ -1340,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(): @@ -1706,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/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 Binary files differnew file mode 100644 index 00000000..e4a8a2d4 --- /dev/null +++ b/askbot/media/jquery-openid/images/mozilla-persona.gif diff --git a/askbot/media/jquery-openid/jquery.openid.js b/askbot/media/jquery-openid/jquery.openid.js index c8e925a3..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(); @@ -310,7 +310,7 @@ $.fn.authenticator = function() { var setup_password_login_or_change = function(provider_name){ var token_name = extra_token_name[provider_name] var password_action_input = $('input[name=password_action]'); - if (userIsAuthenticated === true){ + if (userIsAuthenticated === true && askbot['settings']['useLdapForPasswordLogin'] == false){ var password_button = $('input[name=change_password]'); var submit_action = submit_change_password; if (provider_name === 'local'){ @@ -334,8 +334,7 @@ $.fn.authenticator = function() { password_action_input.val('change_password'); var focus_input = $('#id_new_password'); var submittable_input = $('#id_new_password_retyped'); - } - else{ + } else { $('#password-heading>span').html(token_name); var password_button = $('input[name=login_with_password]'); var submit_action = submit_login_with_password; @@ -382,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'), @@ -404,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 a026a842..d077ce9e 100644 --- a/askbot/media/js/post.js +++ b/askbot/media/js/post.js @@ -83,40 +83,6 @@ function setupFormValidation(form, validationRules, validationMessages, onSubmit }); } -/** - * generic tag cleaning function, settings - * are from askbot live settings and askbot.const - */ -var cleanTag = function(tag_name, settings) { - var tag_regex = new RegExp(settings['tag_regex']); - if (tag_regex.test(tag_name) === false) { - var firstChar = tag_name.substring(0, 1); - if (settings['tag_forbidden_first_chars'].indexOf(firstChar) > -1) { - throw settings['messages']['wrong_first_char']; - } else { - throw settings['messages']['wrong_chars']; - } - } - - var max_length = settings['max_tag_length']; - if (tag_name.length > max_length) { - throw interpolate( - ngettext( - 'must be shorter than %(max_chars)s character', - 'must be shorter than %(max_chars)s characters', - max_length - ), - {'max_chars': max_length }, - true - ); - } - if (settings['force_lowercase_tags']) { - return tag_name.toLowerCase(); - } else { - return tag_name; - } -}; - var validateTagLength = function(value){ var tags = getUniqueWords(value); var are_tags_ok = true; @@ -2426,7 +2392,10 @@ PostCommentsWidget.prototype.getActivateHandler = function(){ } else { //if user can't post, we tell him something and refuse - if (askbot['data']['userIsAuthenticated']) { + if (askbot['settings']['readOnlyModeEnabled'] === true) { + var message = askbot['messages']['readOnlyMessage']; + showMessage(button, message, 'after'); + } else if (askbot['data']['userIsAuthenticated']) { me.startNewComment(); } else { var message = gettext('please sign in or register to post comments'); @@ -2537,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/tag_selector.js b/askbot/media/js/tag_selector.js index dfc8a29a..c905ab86 100644 --- a/askbot/media/js/tag_selector.js +++ b/askbot/media/js/tag_selector.js @@ -274,10 +274,16 @@ function pickedTags(){ }); } + var tagSettings = JSON.parse(askbot['settings']['tag_editor']); var clean_tagnames = []; $.each(tagnames, function(idx, tagname){ if (!(tagname in to_target)){ - clean_tagnames.push(tagname); + try { + cleanTag(tagname, tagSettings); + clean_tagnames.push(tagname); + } catch (e) { + alert(e); + } } }); diff --git a/askbot/media/js/utils.js b/askbot/media/js/utils.js index 7e7b2935..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, '/'); @@ -44,6 +59,41 @@ var getNewUniqueInt = function() { return num; }; +/** + * generic tag cleaning function, settings + * are from askbot live settings and askbot.const + */ +var cleanTag = function(tag_name, settings) { + var tag_regex = new RegExp(settings['tag_regex']); + if (tag_regex.test(tag_name) === false) { + var firstChar = tag_name.substring(0, 1); + if (settings['tag_forbidden_first_chars'].indexOf(firstChar) > -1) { + throw settings['messages']['wrong_first_char']; + } else { + throw settings['messages']['wrong_chars']; + } + } + + var max_length = settings['max_tag_length']; + if (tag_name.length > max_length) { + throw interpolate( + ngettext( + 'must be shorter than %(max_chars)s character', + 'must be shorter than %(max_chars)s characters', + max_length + ), + {'max_chars': max_length }, + true + ); + } + if (settings['force_lowercase_tags']) { + return tag_name.toLowerCase(); + } else { + return tag_name; + } +}; + + var getSingletonController = function(controllerClass, name) { askbot['controllers'] = askbot['controllers'] || {}; var controller = askbot['controllers'][name]; @@ -242,6 +292,7 @@ var notify = function() { }; }(); + /* **************************************************** */ // Search query-string manipulation utils /* **************************************************** */ @@ -1339,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')); @@ -2750,10 +2807,9 @@ Tag.prototype.createDom = function(){ } this._inner_element.addClass('tag tag-right'); this._inner_element.attr('rel', 'tag'); - if (this._title === null){ - this.setTitle( - interpolate(gettext("see questions tagged '%s'"), [this.getName()]) - ); + if (this._title === null) { + var name = this.getName(); + this.setTitle(interpolate(gettext("see questions tagged '%s'"), [name,])); } this._inner_element.attr('title', this._title); this._inner_element.html(this.getDisplayTagName()); diff --git a/askbot/media/js/wmd/wmd.js b/askbot/media/js/wmd/wmd.js index 6f7505d0..f84cd6e4 100644 --- a/askbot/media/js/wmd/wmd.js +++ b/askbot/media/js/wmd/wmd.js @@ -122,14 +122,15 @@ Attacklab.wmdBase = function(){ // Adds a listener callback to a DOM element which is fired on a specified // event. util.addEvent = function(elem, event, listener){ - if (elem && elem.attachEvent) { - // IE only. The "on" is mandatory. - elem.attachEvent("on" + event, listener); - } - else { - // Other browsers. - elem.addEventListener(event, listener, false); - } + if (elem) { + if (elem.attachEvent) { + // IE only. The "on" is mandatory. + elem.attachEvent("on" + event, listener); + } else { + // Other browsers. + elem.addEventListener(event, listener, false); + } + } }; diff --git a/askbot/media/style/style.css b/askbot/media/style/style.css index 21ded8e6..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: -50px; + top: 42px; margin: 0; outline: 1px solid #dadad4; position: absolute; z-index: 10000; } -.search-drop-menu.waiting { - bottom: -27px; -} .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; } @@ -3555,6 +3590,14 @@ a.offensive { .message p { margin-bottom: 0px; } +.system-messages { + color: red; + background: yellow; + font-size: 21px; + font-weight: bold; + line-height: 25px; + padding: 0 5px; +} p.space-above { margin-top: 10px; } @@ -3851,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; } @@ -4339,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; } @@ -4773,9 +4801,6 @@ body.lang-fi .box .inputs #subscribedTagInput, body.lang-fi .box .inputs #ab-tag-search { width: 142px; } -body.lang-ru #scopeNav { - width: 315px; -} body.lang-ru .scope-selector { font-size: 17px; } diff --git a/askbot/media/style/style.less b/askbot/media/style/style.less index abde2e14..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: -50px; + top: 42px; margin: 0; outline: 1px solid #dadad4; position: absolute; z-index: 10000; - &.waiting { - bottom: -27px; - } - 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 { @@ -3768,6 +3805,15 @@ a.offensive { margin-bottom: 0px; } +.system-messages { + color: red; + background: yellow; + font-size: 21px; + font-weight: bold; + line-height: 25px; + padding: 0 5px; +} + p.space-above { margin-top: 10px; } @@ -4116,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; } @@ -4556,6 +4580,10 @@ td.setting-input { } } +#recaptcha_area, #recaptcha_table { + line-height: 0!important; +} + .acInput { width: 200px; } @@ -5026,9 +5054,6 @@ body.lang-fi { } body.lang-ru { - #scopeNav { - width: 315px; - } .scope-selector { font-size: 17px; } diff --git a/askbot/middleware/forum_mode.py b/askbot/middleware/forum_mode.py index 989f0a92..5fd2bda3 100644 --- a/askbot/middleware/forum_mode.py +++ b/askbot/middleware/forum_mode.py @@ -8,6 +8,7 @@ from django.conf import settings from django.core.urlresolvers import resolve from askbot.shims.django_shims import ResolverMatch from askbot.conf import settings as askbot_settings +import urllib PROTECTED_VIEW_MODULES = ( 'askbot.views', @@ -63,5 +64,9 @@ class ForumModeMiddleware(object): _('Please log in to use %s') % \ askbot_settings.APP_SHORT_NAME ) - return HttpResponseRedirect(settings.LOGIN_URL) + redirect_url = '%s?next=%s' % ( + settings.LOGIN_URL, + urllib.quote_plus(request.get_full_path()) + ) + return HttpResponseRedirect(redirect_url) return None 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 69754d7f..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&d=%(type)s&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) @@ -3042,13 +3096,13 @@ def format_instant_notification_email( ) #todo: remove hardcoded style else: - content_preview = post.format_for_email(is_leaf_post = True) + content_preview = post.format_for_email(is_leaf_post=True, recipient=to_user) #add indented summaries for the parent posts - content_preview += post.format_for_email_as_parent_thread_summary() + content_preview += post.format_for_email_as_parent_thread_summary(recipient=to_user) #content_preview += '<p>======= Full thread summary =======</p>' - #content_preview += post.thread.format_for_email(user=to_user) + #content_preview += post.thread.format_for_email(recipient=to_user) if update_type == 'post_shared': user_action = _('%(user)s shared a %(post_link)s.') @@ -3072,10 +3126,22 @@ def format_instant_notification_email( post_url = site_url(post.get_absolute_url()) user_url = site_url(from_user.get_absolute_url()) + + 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) + user_action = user_action % { - 'user': '<a href="%s">%s</a>' % (user_url, from_user.username), + 'user': user_link, 'post_link': '<a href="%s">%s</a>' % (post_url, _(post.post_type)) - #'post_link': '%s <a href="%s">>>></a>' % (_(post.post_type), post_url) } can_reply = to_user.can_post_by_email() @@ -3632,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: @@ -3651,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 @@ -3673,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) @@ -3691,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 9618690d..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) @@ -958,7 +962,8 @@ class Post(models.Model): return filtered_candidates def format_for_email( - self, quote_level=0, is_leaf_post=False, format=None + self, quote_level=0, is_leaf_post=False, format=None, + recipient=None ): """format post for the output in email, if quote_level > 0, the post will be indented that number of times @@ -969,13 +974,14 @@ class Post(models.Model): template = get_template('email/quoted_post.html') data = { 'post': self, + 'recipient': recipient, 'quote_level': quote_level, 'is_leaf_post': is_leaf_post, 'format': format } return template.render(Context(data))#todo: set lang - def format_for_email_as_parent_thread_summary(self): + def format_for_email_as_parent_thread_summary(self, recipient=None): """format for email as summary of parent posts all the way to the original question""" quote_level = 0 @@ -987,20 +993,25 @@ class Post(models.Model): break quote_level += 1 output += parent_post.format_for_email( - quote_level = quote_level, - format = 'parent_subthread' + quote_level=quote_level, + format='parent_subthread', + recipient=recipient ) current_post = parent_post return output - def format_for_email_as_subthread(self): + def format_for_email_as_subthread(self, recipient=None): """outputs question or answer and all it's comments returns empty string for all other post types """ from django.template import Context from django.template.loader import get_template template = get_template('email/post_as_subthread.html') - return template.render(Context({'post': self}))#todo: set lang + data = { + 'post': self, + 'recipient': recipient + } + return template.render(Context(data))#todo: set lang def set_cached_comments(self, comments): """caches comments in the lifetime of the object @@ -1084,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( @@ -1469,6 +1481,8 @@ class Post(models.Model): ) elif user.email_tag_filter_strategy == const.INCLUDE_ALL: return True + elif user.email_tag_filter_strategy == const.INCLUDE_SUBSCRIBED: + return user.has_affinity_to_question(question, affinity_type='like') else: raise ValueError( 'unexpected User.email_tag_filter_strategy %s'\ diff --git a/askbot/models/question.py b/askbot/models/question.py index a1513528..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 @@ -14,6 +15,7 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ungettext from django.utils.translation import string_concat from django.utils.translation import get_language +from django.utils.translation import activate as activate_language import askbot from askbot.conf import settings as askbot_settings @@ -23,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 @@ -121,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: @@ -397,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] @@ -583,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") @@ -607,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') @@ -817,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") @@ -839,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: @@ -856,13 +925,13 @@ class Thread(models.Model): else: return self.title - def format_for_email(self, user=None): + def format_for_email(self, recipient=None): """experimental function: output entire thread for email""" question, answers, junk, published_ans_ids = \ - self.get_cached_post_data(user=user) + self.get_cached_post_data(user=recipient) - output = question.format_for_email_as_subthread() + output = question.format_for_email_as_subthread(recipient=recipient) if answers: #todo: words answer_heading = ungettext( @@ -872,7 +941,7 @@ class Thread(models.Model): ) % {'count': len(answers)} output += '<p>%s</p>' % answer_heading for answer in answers: - output += answer.format_for_email_as_subthread() + output += answer.format_for_email_as_subthread(recipient=recipient) return output def get_answers_by_user(self, user): @@ -1117,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( @@ -1152,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 @@ -1300,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 @@ -1328,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() @@ -1352,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) @@ -1541,6 +1618,7 @@ class Thread(models.Model): } from askbot.views.context import get_extra as get_extra_context context.update(get_extra_context('ASKBOT_QUESTION_SUMMARY_EXTRA_CONTEXT', None, context)) + activate_language(self.language_code) html = get_template('widgets/question_summary.html').render(context) # INFO: Timeout is set to 30 days: # * timeout=0/None is not a reliable cross-backend way to set infinite timeout 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 21338a0a..ea6e3905 100644 --- a/askbot/setup_templates/urls.py +++ b/askbot/setup_templates/urls.py @@ -2,11 +2,12 @@ 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 handler500 -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, handler500 + from django.conf.urls import include, patterns, url +except ImportError: + from django.conf.urls.defaults import handler404, handler500 + from django.conf.urls.defaults import include, patterns, url from django.conf import settings from django.contrib import admin diff --git a/askbot/tasks.py b/askbot/tasks.py index 2f04ce12..ec3e405b 100644 --- a/askbot/tasks.py +++ b/askbot/tasks.py @@ -26,6 +26,7 @@ from django.contrib.contenttypes.models import ContentType from django.template import Context from django.template.loader import get_template from django.utils.translation import ugettext as _ +from django.utils.translation import activate as activate_language from django.utils import simplejson from celery.decorators import task from askbot.conf import settings as askbot_settings @@ -111,6 +112,7 @@ def notify_author_of_published_revision_celery_task(revision): } #load the template + activate_language(revision.post.language_code) template = get_template('email/notify_author_about_approved_post.html') #todo: possibly add headers to organize messages in threads headers = {'Reply-To': append_content_address} @@ -248,6 +250,7 @@ def send_instant_notifications_about_activity_in_post( reply_address, alt_reply_address = get_reply_to_addresses(user, post) + activate_language(post.language_code) subject_line, body_text = format_instant_notification_email( to_user = user, from_user = update_activity.user, 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" /> <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 e85dafd6..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 %} @@ -36,6 +39,7 @@ var userIsAuthenticated = false; {% endif %} askbot['settings']['signin_always_show_local_login'] = {% if settings.SIGNIN_ALWAYS_SHOW_LOCAL_LOGIN %}true{% else %}false{% endif %}; + askbot['settings']['useLdapForPasswordLogin'] = {{ settings.USE_LDAP_FOR_PASSWORD_LOGIN|as_js_bool}}; $("body").authenticator(); (function() { var form = new ChangePasswordForm(); diff --git a/askbot/templates/authopenid/signin.html b/askbot/templates/authopenid/signin.html index 00a0bb10..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,
@@ -73,7 +74,7 @@ {% endif %}
>
{{login_form.password_action}}
- {% if user.is_anonymous() %}
+ {% if user.is_anonymous() or settings.USE_LDAP_FOR_PASSWORD_LOGIN %}
{% if have_buttons %}
<h2 id="password-heading">
{% trans %}or enter your <span>user name and password</span>{% endtrans %}
@@ -92,7 +93,13 @@ </tr>
{% endif %}
<tr>
- <td><label for="id_username">{% trans %}Login or email{% endtrans %}</label></td>
+ <td><label for="id_username">
+ {% if settings.USE_LDAP_FOR_PASSWORD_LOGIN %}
+ {% trans %}Login{% endtrans %}
+ {% else %}
+ {% trans %}Login or email{% endtrans %}
+ {% endif %}
+ </label></td>
<td>{{login_form.username}}</td>
</tr>
<tr>
@@ -106,7 +113,7 @@ <a class="create-password-account" style="vertical-align:middle" href="{% url user_signup_with_password %}?login_provider=local">{% trans %}Create a password-protected account{% endtrans %}</a>
{% endif %}
</p>
- {% elif settings.USE_LDAP_FOR_PASSWORD_LOGIN == False %}
+ {% else %}{# change password form #}
<h2 id="password-heading">
{% trans %}To change your password - please enter the new one twice, then submit{% endtrans %}
</h2>
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" /> - <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 78816c4f..c5b7cfcc 100644 --- a/askbot/templates/email/macros.html +++ b/askbot/templates/email/macros.html @@ -1,5 +1,10 @@ +{%- macro mailto_link(user) -%} + <a href="mailto:{{ user.email }}">{{ user.email }}</a> +{%- endmacro -%} + {% macro quoted_post( post=None, + recipient=None, quote_level=0, format=None, is_leaf_post=False @@ -7,44 +12,48 @@ %} {% spaceless %} {{ start_quote(quote_level) }} - {% if post.is_anonymous %} - {% set author = settings.NAME_OF_ANONYMOUS_USER %} + + {% if recipient|can_see_private_user_data(post.author) %} + {% set author = post.author.username|escape ~ '/(' ~ mailto_link(post.author) ~ ')' %} + {% elif post.is_anonymous %} + {% set author = post.author.get_name_of_anonymous_user()|escape %} {% else %} {% set author = post.author.username|escape %} {% 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/email/post_as_subthread.html b/askbot/templates/email/post_as_subthread.html index 08b35ac5..5c08247f 100644 --- a/askbot/templates/email/post_as_subthread.html +++ b/askbot/templates/email/post_as_subthread.html @@ -1,7 +1,7 @@ {% from "email/macros.html" import quoted_post %} {% if post.post_type in ('question', 'answer') %} - {{ quoted_post(post) }} + {{ quoted_post(post, recipient=recipient) }} {% set comments = post.get_cached_comments() %} {% if comments %} <p> @@ -12,7 +12,7 @@ {%- endtrans -%} </p> {% for comment in comments %} - {{ quoted_post(comment, quote_level = 1) }} + {{ quoted_post(comment, quote_level=1, recipient=recipient) }} {% endfor %} {% endif %} {% endif %} diff --git a/askbot/templates/email/quoted_post.html b/askbot/templates/email/quoted_post.html index a1741ed3..2c3b6046 100644 --- a/askbot/templates/email/quoted_post.html +++ b/askbot/templates/email/quoted_post.html @@ -1,7 +1,8 @@ {% from "email/macros.html" import quoted_post %} {{ quoted_post( - post, - quote_level, + post=post, + quote_level=quote_level, + recipient=recipient, is_leaf_post=is_leaf_post, format=format ) 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/faq_static.html b/askbot/templates/faq_static.html index 22b1dbe4..20a090d2 100644 --- a/askbot/templates/faq_static.html +++ b/askbot/templates/faq_static.html @@ -18,7 +18,7 @@ {% trans %}Karma system allows users to earn rights to perform a variety of moderation tasks{% endtrans %} </p> <h2>{% trans %}How does karma system work?{% endtrans %}</h2> -<p>{% trans %}When a question or answer is upvoted, the user who posted them will gain some points, which are called \"karma points\". These points serve as a rough measure of the community trust to him/her. Various moderation tasks are gradually assigned to the users based on those points.{% endtrans %}</p> +<p>{% trans %}When a question or answer is upvoted, the user who posted them will gain some points, which are called "karma points". These points serve as a rough measure of the community trust to him/her. Various moderation tasks are gradually assigned to the users based on those points.{% endtrans %}</p> <p>{% trans MAX_REP_GAIN_PER_USER_PER_DAY=settings.MAX_REP_GAIN_PER_USER_PER_DAY, REP_GAIN_FOR_RECEIVING_UPVOTE=settings.REP_GAIN_FOR_RECEIVING_UPVOTE, REP_LOSS_FOR_RECEIVING_DOWNVOTE=settings.REP_LOSS_FOR_RECEIVING_DOWNVOTE|absolute_value %}For example, if you ask an interesting question or give a helpful answer, your input will be upvoted. On the other hand if the answer is misleading - it will be downvoted. Each vote in favor will generate <strong>{{REP_GAIN_FOR_RECEIVING_UPVOTE}}</strong> points, each vote against will subtract <strong>{{REP_LOSS_FOR_RECEIVING_DOWNVOTE}}</strong> points. There is a limit of <strong>{{MAX_REP_GAIN_PER_USER_PER_DAY}}</strong> points that can be accumulated for a question or answer per day. The table below explains reputation point requirements for each type of moderation task.{% endtrans %} </p> 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 %}"/> - <input type="submit" class="submit cancel" name="cancel" value="{% trans %}Cancel{% endtrans %}"/> + <input type="submit" value="{% trans %}Send Feedback{% endtrans %}"/> + <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 326a604f..b314255c 100644 --- a/askbot/templates/macros.html +++ b/askbot/templates/macros.html @@ -59,7 +59,9 @@ {% else %} {{ user_card(user, karma_mode=karma_mode, badges_mode=badges_mode) }} {% endif %} - {{ user_primary_group(user) }} + {% if settings.GROUPS_ENABLED %} + {{ user_primary_group(user) }} + {% endif %} {%- endmacro -%} {%- macro post_last_updater_and_creator_info( @@ -386,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 %} @@ -449,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> @@ -641,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 -%} @@ -845,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 24182708..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 %}'; @@ -17,6 +13,7 @@ askbot['urls']['mark_subscribed_tag'] = '{% url mark_subscribed_tag %}'; askbot['urls']['unmark_tag'] = '{% url unmark_tag %}'; askbot['urls']['set_tag_filter_strategy'] = '{% url "set_tag_filter_strategy" %}'; + askbot['settings']['tag_editor'] = '{{ tag_editor_settings|escapejs }}'; if (Modernizr.history) { // history management works! 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&subset=latin-ext' rel='stylesheet' type='text/css'> - <link href='//fonts.googleapis.com/css?family=Open+Sans+Condensed:700&subset=cyrillic-ext' rel='stylesheet' type='text/css'> + <link href='//fonts.googleapis.com/css?family=Open+Sans+Condensed:700&subset=latin-ext' rel='stylesheet' type='text/css' /> + <link href='//fonts.googleapis.com/css?family=Open+Sans+Condensed:700&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.html b/askbot/templates/question.html index 598eadfc..e1310e3c 100644 --- a/askbot/templates/question.html +++ b/askbot/templates/question.html @@ -15,6 +15,9 @@ //below is pure cross-browser javascript, no jQuery askbot['data']['userIsThreadModerator'] = {% if user_is_thread_moderator %}true{% else %}false{% endif %}; askbot['data']['oldestAnswerId'] = {% if oldest_answer_id %}{{ oldest_answer_id }}{% else %}-1{% endif %}; + {% if settings.READ_ONLY_MODE_ENABLED %} + askbot['settings']['readOnlyModeEnabled'] = true; + {% endif %} (function(){ var hasClass = function(node, selector) { @@ -174,7 +177,37 @@ } } + function remove_all_controls(post_id) { + var deleteBtn = document.getElementById('post-' + post_id + '-delete'); + var controls = deleteBtn.parentNode; + if (controls.className == 'comment-content') { + removeNode(deleteBtn); + var convertLinks = findChildrenByClassName(controls, 'convert-comment'); + if (convertLinks.length) { + removeNode(convertLinks[0]); + } + var editLinks = findChildrenByClassName(controls, 'edit'); + if (editLinks.length) { + removeNode(editLinks[0]); + } + } else { + var buttons = controls.childNodes; + var numButtons = buttons.length; + for (var i = numButtons - 1; i >= 0; i--) { + removeNode(buttons[i]); + } + } + }; + + function render_post_controls(post_id){ + + //in this case remove all post controls + if (askbot['settings']['readOnlyModeEnabled'] === true) { + remove_all_controls(post_id); + return; + } + if (data['userIsAdminOrMod']){ return;//all remaining functions stay on } @@ -337,6 +370,9 @@ askbot['messages']['addComment'] = '{% trans %}add a comment{% endtrans %}'; askbot['messages']['userNamePrompt'] = '{% trans %}User name:{% endtrans %}'; askbot['messages']['userEmailPrompt'] = '{% trans %}Email address:{% endtrans %}'; + {% if settings.READ_ONLY_MODE_ENABLED %} + askbot['messages']['readOnlyMessage'] = '{{ settings.READ_ONLY_MESSAGE }}'; + {% endif %} askbot['settings']['saveCommentOnEnter'] = {{ settings.SAVE_COMMENT_ON_ENTER|as_js_bool }}; askbot['settings']['tagSource'] = '{{ settings.TAG_SOURCE }}'; askbot['settings']['enableSharingGoogle'] = {{ settings.ENABLE_SHARING_GOOGLE|as_js_bool }}; 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 3c790187..9e8644d4 100644 --- a/askbot/templates/question/new_answer_form.html +++ b/askbot/templates/question/new_answer_form.html @@ -12,7 +12,7 @@ >{{ settings.WORDS_LOGIN_SIGNUP_TO_ANSWER|escape }}</a> {% endif %} {% else %} - {% if not thread.closed %} + {% if not thread.closed and settings.READ_ONLY_MODE_ENABLED == False %} <div> {% spaceless %} <h2> @@ -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" /> + <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" /> - <input type="button" value="{% trans %}Cancel{% endtrans %}" class="submit" onclick="history.back(-1);" /> + <input type="submit" value="{% trans %}Retag{% endtrans %}"/> + <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" /> - <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" > - <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 %}"/> - <input type="submit" class="submit" name="stop_email" value="{% trans %}Stop Email{% endtrans %}"/> + <input type="submit" name="save" value="{% trans %}Update{% endtrans %}"/> + <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 a732579d..6a9ff91e 100644 --- a/askbot/templates/user_profile/user_info.html +++ b/askbot/templates/user_profile/user_info.html @@ -55,6 +55,12 @@ <td><b>{{view_user.real_name|escape}}</b></td> </tr> {% endif %} + {% if request.user|can_see_private_user_data(view_user) %} + <tr> + <td>{% trans %}email{% endtrans %}</td> + <td>{{view_user.email}}</td> + </tr> + {% endif %} {% if settings.GROUPS_ENABLED %} <tr> <td>{% trans %}groups{% endtrans %}</td> @@ -119,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 %}" /> - <input type="submit" class="submit" name="add_reputation" value="{% trans %}Add{% endtrans %}" /> + <input type="submit" name="subtract_reputation" value="{% trans %}Subtract{% endtrans %}" /> + <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 0d3e9e30..dec52d79 100644 --- a/askbot/templates/widgets/ask_form.html +++ b/askbot/templates/widgets/ask_form.html @@ -61,16 +61,14 @@ </div> {% endif %} </div> - - {% if form.recaptcha.errors %} - <span class="form-error">{{ form.recaptcha.errors }}</span> + {% if form.recaptcha %} + <div>{{ macros.form_field_with_errors(form.recaptcha) }}</div> + <div class="clearfix"></div> {% endif %} - {{ form.recaptcha }} - - {% if request.user.is_authenticated() or form.recaptcha %} - <input type="submit" name="post" value="{{ settings.WORDS_ASK_YOUR_QUESTION|escape }}" class="submit" /> + {% 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_anon" value="{% trans %}Login/Signup to Post{% endtrans %}" 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/header.html b/askbot/templates/widgets/header.html index 52e528bc..50a25564 100644 --- a/askbot/templates/widgets/header.html +++ b/askbot/templates/widgets/header.html @@ -16,5 +16,10 @@ </div> <div class="clean"></div> </div> + {% if settings.READ_ONLY_MODE_ENABLED %} + <div class="content-wrapper system-messages"> + {{ settings.READ_ONLY_MESSAGE }} + </div> + {% endif %} </div> <!-- end template header.html --> diff --git a/askbot/templates/widgets/question_edit_tips.html b/askbot/templates/widgets/question_edit_tips.html index 78f6d3fb..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() and not (settings.USE_RECAPTCHA and settings.ALLOW_ASK_ANONYMOUSLY) %} + {% 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_filters_jinja.py b/askbot/templatetags/extra_filters_jinja.py index 438cfa14..0d7ab0c8 100644 --- a/askbot/templatetags/extra_filters_jinja.py +++ b/askbot/templatetags/extra_filters_jinja.py @@ -95,6 +95,13 @@ def strip_path(url): return url_utils.strip_path(url) @register.filter +def can_see_private_user_data(viewer, target): + if viewer.is_authenticated() and viewer.is_administrator_or_moderator(): + #todo: take into account intersection of viewer and target user groups + return askbot_settings.SHOW_ADMINS_PRIVATE_USER_DATA + return False + +@register.filter def clean_login_url(url): """pass through, unless user was originally on the logout page""" try: 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&d=%(gravatar_type)s&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 618562e7..566173ce 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -4,8 +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 -from django.conf.urls.defaults import handler500, handler404 + +try: + from django.conf.urls import url, patterns, include + from django.conf.urls import handler500, handler404 +except ImportError: + from django.conf.urls.defaults import url, patterns, include + from django.conf.urls.defaults import handler500, handler404 + from django.contrib import admin from askbot import views from askbot.feed import RssLastestQuestionsFeed, RssIndividualQuestionFeed @@ -119,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/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 9392d887..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, @@ -263,7 +261,9 @@ def questions(request, **kwargs): request, template_data ) + template_data.update(extra_context) + template_data.update(context.get_for_tag_editor()) #and one more thing:) give admin user heads up about #setting the domain name if they have not done that yet @@ -298,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() @@ -321,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 @@ -371,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 5c4042fa..41cf4c8f 100644 --- a/askbot/views/widgets.py +++ b/askbot/views/widgets.py @@ -60,6 +60,10 @@ def ask_widget(request, widget_id): widget = get_object_or_404(models.AskWidget, id=widget_id) if request.method == "POST": + + if askbot_settings.READ_ONLY_MODE_ENABLED: + return redirect('ask_by_widget') + form = forms.AskWidgetForm( include_text=widget.include_text_field, data=request.POST, @@ -241,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) @@ -254,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 c1251850..a11fb941 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -128,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 @@ -194,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() @@ -206,7 +206,6 @@ def import_data(request): return render(request, 'import_data.html', data) @fix_recaptcha_remote_ip -#@login_required #actually you can post anonymously, but then must register @csrf.csrf_protect @decorators.check_authorization_to_post(ugettext_lazy('Please log in to make posts')) @decorators.check_spam('text') @@ -223,8 +222,11 @@ def ask(request):#view used to ask a new question request.user.message_set.create(message=_('Sorry, but you have only read access')) return HttpResponseRedirect(referer) - form = forms.AskForm(request.POST, user=request.user) + if askbot_settings.READ_ONLY_MODE_ENABLED: + return HttpResponseRedirect(reverse('index')) + 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,20 +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 ( request.user.is_authenticated() - or ( askbot_settings.USE_RECAPTCHA - and askbot_settings.ALLOW_ASK_ANONYMOUSLY)): - - if not request.user.is_authenticated(): - ask_anonymously = True - user = User.objects.filter(is_staff=True)[0].get_or_create_fake_user("anonymous", "anonymous@example.com") - else: - user = form.get_post_user(request.user) + if user: try: question = user.post_question( title=title, @@ -357,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(): @@ -366,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) @@ -383,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()) @@ -391,12 +389,18 @@ 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 """ question = get_object_or_404(models.Post, id=id) + + if askbot_settings.READ_ONLY_MODE_ENABLED: + return HttpResponseRedirect(question.get_absolute_url()) + revision = question.get_latest_revision() revision_form = None + try: request.user.assert_can_edit_question(question) if request.method == 'POST': @@ -437,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'] @@ -458,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" @@ -495,8 +500,13 @@ 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) + + if askbot_settings.READ_ONLY_MODE_ENABLED: + return HttpResponseRedirect(answer.get_absolute_url()) + revision = answer.get_latest_revision() class_path = getattr(settings, 'ASKBOT_EDIT_ANSWER_FORM', None) @@ -586,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 @@ -595,6 +606,10 @@ def answer(request, id, form_class=forms.AnswerForm):#process a new answer authenticated users post directly """ question = get_object_or_404(models.Post, post_type='question', id=id) + + if askbot_settings.READ_ONLY_MODE_ENABLED: + return HttpResponseRedirect(question.get_absolute_url()) + if request.method == "POST": #this check prevents backward compatilibility @@ -686,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') @@ -732,6 +747,10 @@ def post_comments(request):#generic ajax handler to load comments to an object '<a href="%(sign_in_url)s">sign in</a>.') % \ {'sign_in_url': url_utils.get_login_url()} raise exceptions.PermissionDenied(msg) + + if askbot_settings.READ_ONLY_MODE_ENABLED: + raise exceptions.PermissionDenied(askbot_settings.READ_ONLY_MESSAGE) + comment = user.post_comment( parent_post=post, body_text=form.cleaned_data['comment'] ) @@ -742,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 @@ -753,6 +772,9 @@ def edit_comment(request): if request.user.is_anonymous(): raise exceptions.PermissionDenied(_('Sorry, anonymous users cannot edit comments')) + if askbot_settings.READ_ONLY_MODE_ENABLED: + raise exceptions.PermissionDenied(askbot_settings.READ_ONLY_MESSAGE) + form = forms.EditCommentForm(request.POST) if form.is_valid() == False: raise exceptions.PermissionDenied('This content is forbidden') @@ -807,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() @@ -816,6 +838,9 @@ def delete_comment(request): comment = get_object_or_404(models.Post, post_type='comment', id=comment_id) request.user.assert_can_delete_comment(comment) + if askbot_settings.READ_ONLY_MODE_ENABLED: + raise exceptions.PermissionDenied(askbot_settings.READ_ONLY_MESSAGE) + parent = comment.parent comment.delete() #attn: recalc denormalized field @@ -834,22 +859,29 @@ 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'] ) - request.user.repost_comment_as_answer(comment) + if askbot_settings.READ_ONLY_MODE_ENABLED is False: + request.user.repost_comment_as_answer(comment) + return HttpResponseRedirect(comment.get_absolute_url()) @decorators.post_only @@ -862,12 +894,24 @@ 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() else: 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 |