diff options
author | Adolfo Fitoria <adolfo.fitoria@gmail.com> | 2012-05-15 15:25:07 -0600 |
---|---|---|
committer | Adolfo Fitoria <adolfo.fitoria@gmail.com> | 2012-05-15 15:25:07 -0600 |
commit | fe2bd64fcf4767c3801af4451e347365d288b15d (patch) | |
tree | b61e513ee6bc0ff06a4a85e4d8f02f42941121b3 | |
parent | 32a4a5ddf93f2bc605706ecc3eafbc63dc923acf (diff) | |
parent | 4cf3ec3998b1f067a7f0c76eae4a90c274024986 (diff) | |
download | askbot-fe2bd64fcf4767c3801af4451e347365d288b15d.tar.gz askbot-fe2bd64fcf4767c3801af4451e347365d288b15d.tar.bz2 askbot-fe2bd64fcf4767c3801af4451e347365d288b15d.zip |
Merge branch 'master' into livesettings_fix
114 files changed, 15898 insertions, 1469 deletions
diff --git a/askbot/__init__.py b/askbot/__init__.py index aaf77511..859b2695 100644 --- a/askbot/__init__.py +++ b/askbot/__init__.py @@ -9,7 +9,7 @@ import smtplib import sys import logging -VERSION = (0, 7, 42) +VERSION = (0, 7, 43) #keys are module names used by python imports, #values - the package qualifier to use for pip @@ -34,6 +34,7 @@ REQUIREMENTS = { 'openid': 'python-openid', 'pystache': 'pystache==0.3.1', 'lamson': 'Lamson', + 'pytz': 'pytz', } #necessary for interoperability of django and coffin diff --git a/askbot/api.py b/askbot/api.py index 8b788016..57d5c1aa 100644 --- a/askbot/api.py +++ b/askbot/api.py @@ -18,8 +18,14 @@ def get_info_on_moderation_items(user): if not(user.is_moderator() or user.is_administrator()): return None + content_types = ( + const.TYPE_ACTIVITY_MARK_OFFENSIVE, + const.TYPE_ACTIVITY_MODERATED_NEW_POST, + const.TYPE_ACTIVITY_MODERATED_POST_EDIT, + ) + messages = models.ActivityAuditStatus.objects.filter( - activity__activity_type = const.TYPE_ACTIVITY_MARK_OFFENSIVE, + activity__activity_type__in = content_types, user = user ) diff --git a/askbot/askbot b/askbot/askbot new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/askbot/askbot diff --git a/askbot/conf/__init__.py b/askbot/conf/__init__.py index dff91d8e..8378fca3 100644 --- a/askbot/conf/__init__.py +++ b/askbot/conf/__init__.py @@ -3,8 +3,10 @@ import askbot import askbot.conf.minimum_reputation import askbot.conf.vote_rules import askbot.conf.reputation_changes +import askbot.conf.karma_and_badges_visibility import askbot.conf.email import askbot.conf.forum_data_rules +import askbot.conf.moderation import askbot.conf.flatpages import askbot.conf.site_settings import askbot.conf.license @@ -17,6 +19,7 @@ import askbot.conf.sidebar_profile import askbot.conf.leading_sidebar import askbot.conf.spam_and_moderation import askbot.conf.user_settings +import askbot.conf.group_settings import askbot.conf.markup import askbot.conf.social_sharing import askbot.conf.badges diff --git a/askbot/conf/forum_data_rules.py b/askbot/conf/forum_data_rules.py index be23eab7..7d98c9e8 100644 --- a/askbot/conf/forum_data_rules.py +++ b/askbot/conf/forum_data_rules.py @@ -187,6 +187,29 @@ settings.register( ) settings.register( + livesettings.BooleanValue( + FORUM_DATA_RULES, + 'SUBSCRIBED_TAG_SELECTOR_ENABLED', + default = False, + description = _('Use separate set for subscribed tags'), + help_text = _( + 'If enabled, users will have a third set of tag selections ' + '- "subscribed" (by email) in additon to "interesting" ' + 'and "ignored"' + ) + ) +) + +settings.register( + livesettings.BooleanValue( + FORUM_DATA_RULES, + 'TAG_SEARCH_INPUT_ENABLED', + default = False, + description = _('Enable separate tag search box on main page') + ) +) + +settings.register( livesettings.IntegerValue( FORUM_DATA_RULES, 'MAX_COMMENTS_TO_SHOW', diff --git a/askbot/conf/group_settings.py b/askbot/conf/group_settings.py new file mode 100644 index 00000000..b3c0069c --- /dev/null +++ b/askbot/conf/group_settings.py @@ -0,0 +1,32 @@ +"""Group settings""" +from askbot.conf.settings_wrapper import settings +from askbot.conf.super_groups import LOGIN_USERS_COMMUNICATION +from askbot.deps import livesettings +from django.utils.translation import ugettext as _ + +GROUP_SETTINGS = livesettings.ConfigurationGroup( + 'GROUP_SETTINGS', + _('Group settings'), + super_group = LOGIN_USERS_COMMUNICATION + ) + +settings.register( + livesettings.BooleanValue( + GROUP_SETTINGS, + 'GROUPS_ENABLED', + default = False, + description = _('Enable user groups'), + ) +) + +settings.register( + livesettings.BooleanValue( + GROUP_SETTINGS, + 'GROUP_EMAIL_ADDRESSES_ENABLED', + default = False, + description = _('Enable group email adddresses'), + help_text = _( + 'If selected, users can post to groups by email "group-name@domain.com"' + ) + ) +) diff --git a/askbot/conf/karma_and_badges_visibility.py b/askbot/conf/karma_and_badges_visibility.py new file mode 100644 index 00000000..4c75cb22 --- /dev/null +++ b/askbot/conf/karma_and_badges_visibility.py @@ -0,0 +1,50 @@ +""" +Settings for making the karma and badge systems visible to +the users at a different degree +""" +from django.utils.translation import ugettext as _ +from askbot.conf.settings_wrapper import settings +from askbot.deps import livesettings +from askbot.conf.super_groups import REP_AND_BADGES + +KARMA_AND_BADGE_VISIBILITY = livesettings.ConfigurationGroup( + 'KARMA_AND_BADGE_VISIBILITY', + _('Karma & Badge visibility'), + super_group = REP_AND_BADGES + ) + + +settings.register( + livesettings.StringValue( + KARMA_AND_BADGE_VISIBILITY, + 'KARMA_MODE', + default = 'public', + choices = ( + ('public', 'show publicly'), + ('private', 'show to owners only'), + ('hidden', 'hide completely'), + ),#todo: later implement hidden mode + description = _("Visibility of karma"), + clear_cache = True, + help_text = _( + "User's karma may be shown publicly or only to the owners" + ) + ) +) + +settings.register( + livesettings.StringValue( + KARMA_AND_BADGE_VISIBILITY, + 'BADGES_MODE', + default = 'public', + choices = ( + ('public', 'show publicly'), + ('hidden', 'hide completely') + ),#todo: later implement private mode + description = _("Visibility of badges"), + clear_cache = True, + help_text = _( + 'Badges can be either publicly shown or completely hidden' + ) + ) +) diff --git a/askbot/conf/moderation.py b/askbot/conf/moderation.py new file mode 100644 index 00000000..9f8e24c7 --- /dev/null +++ b/askbot/conf/moderation.py @@ -0,0 +1,22 @@ +"""Settings to control content moderation""" + +from askbot.conf.settings_wrapper import settings +from askbot.conf.super_groups import DATA_AND_FORMATTING +from askbot.deps.livesettings import ConfigurationGroup +from askbot.deps.livesettings import BooleanValue +from django.utils.translation import ugettext as _ + +MODERATION = ConfigurationGroup( + 'MODERATION', + _('Content moderation'), + super_group = DATA_AND_FORMATTING + ) + +settings.register( + BooleanValue( + MODERATION, + 'ENABLE_CONTENT_MODERATION', + default = False, + description = _('Enable content moderation'), + ) +) diff --git a/askbot/conf/settings_wrapper.py b/askbot/conf/settings_wrapper.py index 78d16397..b6b5f302 100644 --- a/askbot/conf/settings_wrapper.py +++ b/askbot/conf/settings_wrapper.py @@ -58,8 +58,14 @@ class ConfigSettings(object): self.update(key, self.get_default(key)) def update(self, key, value): - setting = config_get(self.__group_map[key], key) - setting.update(value) + try: + setting = config_get(self.__group_map[key], key) + setting.update(value) + except: + from askbot.deps.livesettings.models import Setting + setting = Setting.objects.get(key=key) + setting.value = value + setting.save() #self.prime_cache() def register(self, value): diff --git a/askbot/conf/skin_general_settings.py b/askbot/conf/skin_general_settings.py index 6abee90a..323550ce 100644 --- a/askbot/conf/skin_general_settings.py +++ b/askbot/conf/skin_general_settings.py @@ -12,7 +12,7 @@ from askbot.conf.super_groups import CONTENT_AND_UI GENERAL_SKIN_SETTINGS = ConfigurationGroup( 'GENERAL_SKIN_SETTINGS', - _('Logos and HTML <head> parts'), + _('Skin, logos and HTML <head> parts'), super_group = CONTENT_AND_UI ) @@ -54,6 +54,7 @@ LANGUAGE_CHOICES = ( ('zh_TW', _("Chinese (Taiwan)")), ) +""" settings.register( values.StringValue( GENERAL_SKIN_SETTINGS, @@ -63,6 +64,7 @@ settings.register( description = _('Select Language'), ) ) +""" settings.register( values.BooleanValue( diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py index 66e20dae..7eef91d1 100644 --- a/askbot/const/__init__.py +++ b/askbot/const/__init__.py @@ -19,6 +19,7 @@ CLOSE_REASONS = ( ) LONG_TIME = 60*60*24*30 #30 days is a lot of time +DATETIME_FORMAT = '%I:%M %p, %d %b %Y' TYPE_REPUTATION = ( (1, 'gain_by_upvoted'), @@ -51,6 +52,11 @@ POST_SORT_METHODS = ( ('relevance-desc', _('relevance')), ) +POST_TYPES = ('answer', 'comment', 'question', 'tag_wiki', 'reject_reason') + +REPLY_SEPARATOR_TEMPLATE = '==== %(user_action)s %(instruction)s -=-==' +REPLY_SEPARATOR_REGEX = re.compile('==== .* -=-==', re.MULTILINE) + ANSWER_SORT_METHODS = (#no translations needed here 'latest', 'oldest', 'votes' ) @@ -117,6 +123,12 @@ TYPE_ACTIVITY_EMAIL_UPDATE_SENT = 18 TYPE_ACTIVITY_MENTION = 19 TYPE_ACTIVITY_UNANSWERED_REMINDER_SENT = 20 TYPE_ACTIVITY_ACCEPT_ANSWER_REMINDER_SENT = 21 +TYPE_ACTIVITY_CREATE_TAG_WIKI = 22 +TYPE_ACTIVITY_UPDATE_TAG_WIKI = 23 +TYPE_ACTIVITY_MODERATED_NEW_POST = 24 +TYPE_ACTIVITY_MODERATED_POST_EDIT = 25 +TYPE_ACTIVITY_CREATE_REJECT_REASON = 26 +TYPE_ACTIVITY_UPDATE_REJECT_REASON = 27 #TYPE_ACTIVITY_EDIT_QUESTION = 17 #TYPE_ACTIVITY_EDIT_ANSWER = 18 @@ -149,6 +161,27 @@ TYPE_ACTIVITY = ( _('reminder about accepting the best answer sent'), ), (TYPE_ACTIVITY_MENTION, _('mentioned in the post')), + ( + TYPE_ACTIVITY_CREATE_TAG_WIKI, + _('created tag description'), + ), + ( + TYPE_ACTIVITY_UPDATE_TAG_WIKI, + _('updated tag description') + ), + (TYPE_ACTIVITY_MODERATED_NEW_POST, _('made a new post')), + ( + TYPE_ACTIVITY_MODERATED_POST_EDIT, + _('made an edit') + ), + ( + TYPE_ACTIVITY_CREATE_REJECT_REASON, + _('created post reject reason'), + ), + ( + TYPE_ACTIVITY_UPDATE_REJECT_REASON, + _('updated post reject reason') + ), ) @@ -216,10 +249,15 @@ POST_STATUS = { INCLUDE_ALL = 0 EXCLUDE_IGNORED = 1 INCLUDE_INTERESTING = 2 -TAG_FILTER_STRATEGY_CHOICES = ( - (INCLUDE_ALL, _('off')), - (EXCLUDE_IGNORED, _('exclude ignored')), - (INCLUDE_INTERESTING, _('only selected')), +TAG_DISPLAY_FILTER_STRATEGY_CHOICES = ( + (INCLUDE_ALL, _('show all tags')), + (EXCLUDE_IGNORED, _('exclude ignored tags')), + (INCLUDE_INTERESTING, _('only interesting tags')), +) +TAG_EMAIL_FILTER_STRATEGY_CHOICES = ( + (INCLUDE_ALL, _('email for all tags')), + (EXCLUDE_IGNORED, _('exclude ignored tags')), + (INCLUDE_INTERESTING, _('only subscribed tags')), ) NOTIFICATION_DELIVERY_SCHEDULE_CHOICES = ( diff --git a/askbot/deps/livesettings/values.py b/askbot/deps/livesettings/values.py index e5516d8b..95ca1069 100644 --- a/askbot/deps/livesettings/values.py +++ b/askbot/deps/livesettings/values.py @@ -6,6 +6,7 @@ from decimal import Decimal from django import forms from django.conf import settings as django_settings from django.core.exceptions import ImproperlyConfigured +from django.core.cache import cache from django.utils import simplejson from django.utils.datastructures import SortedDict from django.utils.encoding import smart_str @@ -149,6 +150,7 @@ class Value(object): - `hidden` - If true, then render a hidden field. - `default` - If given, then this Value will return that default whenever it has no assocated `Setting`. - `update_callback` - if given, then this value will call the callback whenever updated + - `clear_cache` - if `True` - clear all the caches on updates """ self.group = group self.key = key @@ -159,6 +161,7 @@ class Value(object): self.hidden = kwargs.pop('hidden', False) self.update_callback = kwargs.pop('update_callback', None) self.requires = kwargs.pop('requires', None) + self.clear_cache = kwargs.pop('clear_cache', False) if self.requires: reqval = kwargs.pop('requiresvalue', key) if not is_list_or_tuple(reqval): @@ -366,6 +369,9 @@ class Value(object): s.save() signals.configuration_value_changed.send(self, old_value=current_value, new_value=new_value, setting=self) + + if self.clear_cache: + cache.clear() return True else: diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index 08aae0a2..e005e019 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -1,7 +1,17 @@ Changes in Askbot ================= -0.7.41, 0.7.24 (April 21, 2012) +0.7.43 (May 14, 2012) +--------------------- +* User groups (Evgeny) +* Public/Private/Hidden reputation (Evgeny) +* Enabling/disabling the badges system (Evgeny) +* Created a basic post moderation feature (Evgeny) +* Created a way to specify reasons for rejecting posts in a modal dialog (Evgeny) +* A number of bug fixes (Adolfo Fitoria, Jim Tittsler, + Evgeny Fadeev, Robin Stocker, Radim Řehůřek, Silvio Heuberger) + +0.7.41, 0.7.42 (April 21, 2012) ------------------------------- * Bug fixes diff --git a/askbot/doc/source/contributors.rst b/askbot/doc/source/contributors.rst index 71bc5cc9..81729979 100644 --- a/askbot/doc/source/contributors.rst +++ b/askbot/doc/source/contributors.rst @@ -38,6 +38,7 @@ Programming and documentation * `Radim Řehůřek <https://github.com/piskvorky>`_ * `monkut <https://github.com/monkut>`_ * `Jim Tittsler <http://wikieducator.org/User:JimTittsler>`_ +* Silvio Heuberger Translations ------------ diff --git a/askbot/forms.py b/askbot/forms.py index 1816c202..252c002e 100644 --- a/askbot/forms.py +++ b/askbot/forms.py @@ -1,9 +1,8 @@ import re from django import forms -from askbot import models from askbot import const from django.utils.translation import ugettext_lazy as _ -from django.utils.translation import ungettext_lazy +from django.utils.translation import ungettext_lazy, string_concat from django.utils.text import get_text_list from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType @@ -21,6 +20,14 @@ def cleanup_dict(dictionary, key, empty_value): if key in dictionary and dictionary[key] == empty_value: del dictionary[key] +def format_form_errors(form): + if form.errors: + errors = form.errors.values() + if len(errors) == 1: + return errors[0] + else: + return 'hahahahah' + def clean_marked_tagnames(tagnames): """return two strings - one containing tagnames that are straight names of tags, and the second one @@ -93,6 +100,59 @@ class CountryField(forms.ChoiceField): return None return value +class CountedWordsField(forms.CharField): + + def __init__( + self, min_words = 0, max_words = 9999, field_name = None, + *args, **kwargs + ): + self.min_words = min_words + self.max_words = max_words + self.field_name = field_name + super(CountedWordsField, self).__init__(*args, **kwargs) + + def clean(self, value): + #todo: this field must be adapted to work with Chinese, etc. + #for that we'll have to count characters instead of words + if value is None: + value = '' + + value = value.strip() + + word_count = len(value.split()) + if word_count < self.min_words: + msg = ungettext_lazy( + 'must be > %d word', + 'must be > %d words', + self.min_words - 1 + ) % (self.min_words - 1) + #todo - space is not used in Chinese + raise forms.ValidationError( + string_concat(self.field_name, ' ', msg) + ) + + if word_count > self.max_words: + msg = ungettext_lazy( + 'must be < %d word', + 'must be < %d words', + self.max_words + 1 + ) % (self.max_words + 1) + raise forms.ValidationError( + string_concat(self.field_name, ' ', msg) + ) + return value + + +class DomainNameField(forms.CharField): + def clean(self, value): + #find a better regex, taking into account tlds + domain_re = re.compile(r'[a-zA-Z\d]+(\.[a-zA-Z\d]+)+') + if domain_re.match(value): + return value + else: + raise forms.ValidationError('%s is not a valid domain name' % value) + + class TitleField(forms.CharField): def __init__(self, *args, **kwargs): super(TitleField, self).__init__(*args, **kwargs) @@ -195,6 +255,7 @@ class TagNamesField(forms.CharField): def need_mandatory_tags(self): """true, if list of mandatory tags is not empty""" + from askbot import models return askbot_settings.TAGS_ARE_REQUIRED and len(models.tag.get_mandatory_tags()) > 0 def tag_string_matches(self, tag_string, mandatory_tag): @@ -207,6 +268,7 @@ class TagNamesField(forms.CharField): def mandatory_tag_missing(self, tag_strings): """true, if mandatory tag is not present in the list of ``tag_strings``""" + from askbot import models mandatory_tags = models.tag.get_mandatory_tags() for mandatory_tag in mandatory_tags: for tag_string in tag_strings: @@ -215,6 +277,7 @@ class TagNamesField(forms.CharField): return True def clean(self, value): + from askbot import models value = super(TagNamesField, self).clean(value) data = value.strip() if len(data) < 1: @@ -876,6 +939,10 @@ class EditAnswerForm(forms.Form): self.fields['text'].initial = revision.text self.fields['wiki'].initial = answer.wiki +class EditTagWikiForm(forms.Form): + text = forms.CharField(required = False) + tag_id = forms.IntegerField() + class EditUserForm(forms.Form): email = forms.EmailField( label=u'Email', @@ -966,7 +1033,7 @@ class EditUserForm(forms.Form): class TagFilterSelectionForm(forms.ModelForm): email_tag_filter_strategy = forms.ChoiceField( - choices = const.TAG_FILTER_STRATEGY_CHOICES, + choices = const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES, initial = const.EXCLUDE_IGNORED, label = _('Choose email tag filter'), widget = forms.RadioSelect @@ -1032,6 +1099,7 @@ class EditUserEmailFeedsForm(forms.Form): ) def set_initial_values(self, user=None): + from askbot import models KEY_MAP = dict([(v, k) for k, v in self.FORM_TO_MODEL_MAP.iteritems()]) if user != None: settings = models.EmailFeedSetting.objects.filter(subscriber=user) @@ -1079,6 +1147,7 @@ class EditUserEmailFeedsForm(forms.Form): """ with save_unbound==True will bypass form validation and save initial values """ + from askbot import models changed = False for form_field, feed_type in self.FORM_TO_MODEL_MAP.items(): s, created = models.EmailFeedSetting.objects.get_or_create( @@ -1135,3 +1204,32 @@ class SimpleEmailSubscribeForm(forms.Form): else: email_settings_form = EFF(initial=EFF.NO_EMAIL_INITIAL) email_settings_form.save(user, save_unbound=True) + +class GroupLogoURLForm(forms.Form): + """form for saving group logo url""" + group_id = forms.IntegerField() + image_url = forms.CharField() + +class EditGroupMembershipForm(forms.Form): + """a form for adding or removing users + to and from user groups""" + user_id = forms.IntegerField() + group_name = forms.CharField() + action = forms.CharField() + + def clean_action(self): + """allowed actions are 'add' and 'remove'""" + action = self.cleaned_data['action'] + if action not in ('add', 'remove'): + del self.cleaned_data['action'] + raise forms.ValidationError('invalid action') + return action + +class EditRejectReasonForm(forms.Form): + reason_id = forms.IntegerField(required = False) + title = CountedWordsField( + min_words = 1, max_words = 4, field_name = _('Title') + ) + details = CountedWordsField( + min_words = 6, field_name = _('Description') + ) diff --git a/askbot/lamson_handlers.py b/askbot/lamson_handlers.py index 488d8b12..34f93a7b 100644 --- a/askbot/lamson_handlers.py +++ b/askbot/lamson_handlers.py @@ -3,9 +3,10 @@ from lamson.routing import route, stateless from lamson.server import Relay from django.utils.translation import ugettext as _ from django.core.files.uploadedfile import SimpleUploadedFile -from django.conf import settings -from askbot.models import ReplyAddress +from django.conf import settings as django_settings +from askbot.models import ReplyAddress, Tag from askbot.utils import mail +from askbot.conf import settings as askbot_settings #we might end up needing to use something like this @@ -55,7 +56,12 @@ def is_attachment(part): attachment""" return get_disposition(part) == 'attachment' -def process_attachment(part): +def is_inline_attachment(part): + """True if part content disposition is + inline""" + return get_disposition(part) == 'inline' + +def format_attachment(part): """takes message part and turns it into SimpleUploadedFile object""" att_info = get_attachment_info(part) name = att_info.get('filename', None) @@ -73,33 +79,67 @@ def is_body(part): return True return False -def get_body(message): - """returns plain text body of the message""" - body = message.body() - if body: - return body - for part in message.walk(): - if is_body(part): - return part.body +def get_part_type(part): + if is_body(part): + return 'body' + elif is_attachment(part): + return 'attachment' + elif is_inline_attachment(part): + return 'inline' + +def get_parts(message): + """returns list of tuples (<part_type>, <formatted_part>), + where <part-type> is one of 'body', 'attachment', 'inline' + and <formatted-part> - will be in the directly usable form: + * if it is 'body' - then it will be unicode text + * for attachment - it will be django's SimpleUploadedFile instance + + There may be multiple 'body' parts as well as others + usually the body is split when there are inline attachments present. + """ + + parts = list() + + if message.body(): + parts.append(('body', message.body())) -def get_attachments(message): - """returns a list of file attachments - represented by StringIO objects""" - attachments = list() for part in message.walk(): - if is_attachment(part): - attachments.append(process_attachment(part)) - return attachments + part_type = get_part_type(part) + if part_type == 'body': + part_content = part.body + elif part_type in ('attachment', 'inline'): + part_content = format_attachment(part) + else: + continue + parts.append((part_type, part_content)) + return parts -@route('ask@(host)') +@route('(addr)@(host)', addr = '.+') @stateless -def ASK(message, host = None): - body = get_body(message) - attachments = get_attachments(message) +def ASK(message, host = None, addr = None): + if addr.startswith('reply-'): + return + parts = get_parts(message) from_address = message.From subject = message['Subject']#why lamson does not give it normally? - mail.process_emailed_question(from_address, subject, body, attachments) - + if addr == 'ask': + mail.process_emailed_question(from_address, subject, parts) + else: + if askbot_settings.GROUP_EMAIL_ADDRESSES_ENABLED == False: + return + try: + group_tag = Tag.group_tags.get( + deleted = False, + name_iexact = addr + ) + mail.process_emailed_question( + from_address, subject, parts, tags = [group_tag.name, ] + ) + except Tag.DoesNotExist: + #do nothing because this handler will match all emails + return + except Tag.MultipleObjectsReturned: + return @route('reply-(address)@(host)', address='.+') @stateless @@ -108,7 +148,7 @@ def PROCESS(message, address = None, host = None): and make a post to askbot based on the contents of the email, including the text body and the file attachments""" try: - for rule in settings.LAMSON_FORWARD: + for rule in django_settings.LAMSON_FORWARD: if re.match(rule['pattern'], message.base['to']): relay = Relay(host=rule['host'], port=rule['port'], debug=1) @@ -123,26 +163,11 @@ def PROCESS(message, address = None, host = None): address = address, allowed_from_email = message.From ) - separator = _("======= Reply above this line. ====-=-=") - parts = get_body(message).split(separator) - attachments = get_attachments(message) - if len(parts) != 2 : - error = _("Your message was malformed. Please make sure to qoute \ - the original notification you received at the end of your reply.") + parts = get_parts(message) + if reply_address.was_used: + reply_address.edit_post(parts) else: - reply_part = parts[0] - reply_part = '\n'.join(reply_part.splitlines(True)[:-3]) - #the function below actually posts to the forum - if reply_address.was_used: - reply_address.edit_post( - reply_part.strip(), - attachments = attachments - ) - else: - reply_address.create_reply( - reply_part.strip(), - attachments = attachments - ) + reply_address.create_reply(parts) except ReplyAddress.DoesNotExist: error = _("You were replying to an email address\ unknown to the system or you were replying from a different address from the one where you\ diff --git a/askbot/management/commands/get_tag_stats.py b/askbot/management/commands/get_tag_stats.py index c30643b3..b065a6a1 100644 --- a/askbot/management/commands/get_tag_stats.py +++ b/askbot/management/commands/get_tag_stats.py @@ -2,6 +2,7 @@ import sys import optparse from django.core.management.base import BaseCommand, CommandError from askbot import models +from askbot import const def get_tag_lines(tag_marks, width = 25): output = list() @@ -120,15 +121,30 @@ class Command(BaseCommand): for bad_tag in user.ignored_tags.split(): ignored_tags.append(bad_tag) + subscribed_tags = list() + subscribed_tags.extend( + tag_marks.filter( + reason='subscribed' + ).values_list( + 'tag__name', flat = True + ) + ) + + for subscribed_tag in user.subscribed_tags.split(): + subscribed_tags.append(subscribed_tag) + followed_count = len(followed_tags) ignored_count = len(ignored_tags) - if followed_count == 0 and ignored_count == 0 and print_empty == False: + subscribed_count = len(subscribed_tags) + total_count = followed_count + ignored_count + subscribed_count + if total_count == 0 and print_empty == False: continue if item_count == 0: - print '%-28s %25s %25s' % ('User (id)', 'Interesting tags', 'Ignored tags') - print '%-28s %25s %25s' % ('=========', '================', '============') + print '%-28s %25s %25s %25s' % ('User (id)', 'Interesting tags', 'Ignored tags', 'Subscribed tags') + print '%-28s %25s %25s %25s' % ('=========', '================', '============', '===============') followed_lines = get_tag_lines(followed_tags, width = 25) ignored_lines = get_tag_lines(ignored_tags, width = 25) + subscribed_lines = get_tag_lines(subscribed_tags, width = 25) follow = '*' if user.email_tag_filter_strategy == const.INCLUDE_INTERESTING: @@ -138,7 +154,8 @@ class Command(BaseCommand): [user_string,], followed_lines, ignored_lines, - format_string = '%-28s %25s %25s' + subscribed_lines, + format_string = '%-28s %25s %25s %25s' ) item_count += 1 for line in output_lines: @@ -163,16 +180,23 @@ class Command(BaseCommand): interesting_tags = models.Tag.objects.get_by_wildcards(wk) for tag in interesting_tags: if tag.name not in wild: - wild[tag.name] = [0, 0] + wild[tag.name] = [0, 0, 0] wild[tag.name][0] += 1 wk = user.ignored_tags.strip().split() ignored_tags = models.Tag.objects.get_by_wildcards(wk) for tag in ignored_tags: if tag.name not in wild: - wild[tag.name] = [0, 0] + wild[tag.name] = [0, 0, 0] wild[tag.name][1] += 1 + wk = user.subscribed_tags.strip().split() + subscribed_tags = models.Tag.objects.get_by_wildcards(wk) + for tag in subscribed_tags: + if tag.name not in wild: + wild[tag.name] = [0, 0, 0] + wild[tag.name][2] += 1 + return wild def print_sub_counts(self, print_empty): @@ -185,6 +209,7 @@ class Command(BaseCommand): for tag in tags: wild_follow = 0 wild_ignore = 0 + wild_sub = 0 if tag.name in wild_tags: (wild_follow, wild_ignore) = wild_tags[tag.name] @@ -193,17 +218,22 @@ class Command(BaseCommand): + wild_follow ignore_count = tag_marks.filter(reason='bad').count() \ + wild_ignore + subscribe_count = tag_marks.filter(reason='subscribe').count() \ + + wild_sub follow_str = '%d (%d)' % (follow_count, wild_follow) ignore_str = '%d (%d)' % (ignore_count, wild_ignore) + subscribe_str = '%d (%d)' % (subscribe_count, wild_sub) + counts = (11-len(subscribe_str)) * ' ' + subscribe_str + ' ' counts = (11-len(follow_str)) * ' ' + follow_str + ' ' counts += (11-len(ignore_str)) * ' ' + ignore_str - if follow_count + ignore_count == 0 and print_empty == False: + total_count = follow_count + ignore_count + subscribe_count + if total_count == 0 and print_empty == False: continue if item_count == 0: - print '%-32s %12s %12s' % ('', 'Interesting', 'Ignored ') - print '%-32s %12s %12s' % ('Tag name', 'Total(wild)', 'Total(wild)') - print '%-32s %12s %12s' % ('========', '===========', '===========') + print '%-32s %12s %12s %12s' % ('', 'Subscribed', 'Ignored ', 'Interesting') + print '%-32s %12s %12s %12s' % ('Tag name', 'Total(wild)', 'Total(wild)', 'Total(wild)') + print '%-32s %12s %12s %12s' % ('========', '===========', '===========', '===========') print '%-32s %s' % (tag.name, counts) item_count += 1 diff --git a/askbot/management/commands/send_email_alerts.py b/askbot/management/commands/send_email_alerts.py index 8002ecbf..b7624e21 100644 --- a/askbot/management/commands/send_email_alerts.py +++ b/askbot/management/commands/send_email_alerts.py @@ -136,6 +136,9 @@ class Command(NoArgsCommand): ).exclude( thread__closed=True ).order_by('-thread__last_activity_at') + + if askbot_settings.ENABLE_CONTENT_MODERATION: + base_qs = base_qs.filter(approved = True) #todo: for some reason filter on did not work as expected ~Q(viewed__who=user) | # Q(viewed__who=user,viewed__when__lt=F('thread__last_activity_at')) #returns way more questions than you might think it should diff --git a/askbot/migrations/0088_install__post_view__for__development.py b/askbot/migrations/0088_install__post_view__for__development.py index e948299a..f8de0c50 100644 --- a/askbot/migrations/0088_install__post_view__for__development.py +++ b/askbot/migrations/0088_install__post_view__for__development.py @@ -8,6 +8,8 @@ import askbot class Migration(SchemaMigration): + """This migration was for development only and now it + is effectively a no-op""" def forwards(self, orm): # create_post_view_sql = open( diff --git a/askbot/migrations/0114_auto__add_groupprofile__add_groupmembership__add_field_tag_tag_wiki.py b/askbot/migrations/0114_auto__add_groupprofile__add_groupmembership__add_field_tag_tag_wiki.py new file mode 100644 index 00000000..35986359 --- /dev/null +++ b/askbot/migrations/0114_auto__add_groupprofile__add_groupmembership__add_field_tag_tag_wiki.py @@ -0,0 +1,318 @@ +# -*- 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 model 'GroupProfile' + db.create_table('askbot_groupprofile', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('group_tag', self.gf('django.db.models.fields.related.OneToOneField')(related_name='group_profile', unique=True, to=orm['askbot.Tag'])), + )) + db.send_create_signal('askbot', ['GroupProfile']) + + # Adding model 'GroupMembership' + db.create_table('askbot_groupmembership', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('group', self.gf('django.db.models.fields.related.ForeignKey')(related_name='user_memberships', to=orm['askbot.Tag'])), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='group_memberships', to=orm['auth.User'])), + )) + db.send_create_signal('askbot', ['GroupMembership']) + + # Adding field 'Tag.tag_wiki' + db.add_column(u'tag', 'tag_wiki', + self.gf('django.db.models.fields.related.OneToOneField')(related_name='described_tag', unique=True, null=True, to=orm['askbot.Post']), + keep_default=False) + + def backwards(self, orm): + # Deleting model 'GroupProfile' + db.delete_table('askbot_groupprofile') + + # Deleting model 'GroupMembership' + db.delete_table('askbot_groupmembership') + + # Deleting field 'Tag.tag_wiki' + db.delete_column(u'tag', 'tag_wiki_id') + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.emailfeedsetting': { + 'Meta': {'object_name': 'EmailFeedSetting'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"}) + }, + 'askbot.favoritequestion': { + 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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'}), + '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", '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.postrevision': { + 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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'}), + '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'}), + '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot']
\ No newline at end of file diff --git a/askbot/migrations/0115_auto__chg_field_post_thread.py b/askbot/migrations/0115_auto__chg_field_post_thread.py new file mode 100644 index 00000000..95c6e267 --- /dev/null +++ b/askbot/migrations/0115_auto__chg_field_post_thread.py @@ -0,0 +1,296 @@ +# -*- 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): + + # Changing field 'Post.thread' + db.alter_column('askbot_post', 'thread_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['askbot.Thread'])) + def backwards(self, orm): + + # Changing field 'Post.thread' + db.alter_column('askbot_post', 'thread_id', self.gf('django.db.models.fields.related.ForeignKey')(default='zhopa', to=orm['askbot.Thread'])) + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.emailfeedsetting': { + 'Meta': {'object_name': 'EmailFeedSetting'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"}) + }, + 'askbot.favoritequestion': { + 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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'}), + '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + '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.postrevision': { + 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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'}), + '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'}), + '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot'] diff --git a/askbot/migrations/0116_auto__add_field_groupprofile_logo_url__add_unique_emailfeedsetting_sub.py b/askbot/migrations/0116_auto__add_field_groupprofile_logo_url__add_unique_emailfeedsetting_sub.py new file mode 100644 index 00000000..12f123b3 --- /dev/null +++ b/askbot/migrations/0116_auto__add_field_groupprofile_logo_url__add_unique_emailfeedsetting_sub.py @@ -0,0 +1,304 @@ +# -*- 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 'GroupProfile.logo_url' + db.add_column('askbot_groupprofile', 'logo_url', + self.gf('django.db.models.fields.URLField')(max_length=200, null=True), + keep_default=False) + + # Adding unique constraint on 'EmailFeedSetting', fields ['subscriber', 'feed_type'] + db.create_unique('askbot_emailfeedsetting', ['subscriber_id', 'feed_type']) + + def backwards(self, orm): + # Removing unique constraint on 'EmailFeedSetting', fields ['subscriber', 'feed_type'] + db.delete_unique('askbot_emailfeedsetting', ['subscriber_id', 'feed_type']) + + # Deleting field 'GroupProfile.logo_url' + db.delete_column('askbot_groupprofile', 'logo_url') + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.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.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': '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'}), + '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + '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.postrevision': { + 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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'}), + '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'}), + '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot']
\ No newline at end of file diff --git a/askbot/migrations/0117_auto__add_field_post_approved__add_field_thread_approved__add_field_po.py b/askbot/migrations/0117_auto__add_field_post_approved__add_field_thread_approved__add_field_po.py new file mode 100644 index 00000000..9dc9e1e5 --- /dev/null +++ b/askbot/migrations/0117_auto__add_field_post_approved__add_field_thread_approved__add_field_po.py @@ -0,0 +1,335 @@ +# -*- 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 'Post.approved' + db.add_column('askbot_post', 'approved', + self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True), + keep_default=False) + + # Adding field 'Thread.approved' + db.add_column('askbot_thread', 'approved', + self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True), + keep_default=False) + + # Adding field 'PostRevision.approved' + db.add_column('askbot_postrevision', 'approved', + self.gf('django.db.models.fields.BooleanField')(default=False, db_index=True), + keep_default=False) + + # Adding field 'PostRevision.approved_by' + db.add_column('askbot_postrevision', 'approved_by', + self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True), + keep_default=False) + + # Adding field 'PostRevision.approved_at' + db.add_column('askbot_postrevision', 'approved_at', + self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), + keep_default=False) + + def backwards(self, orm): + # Deleting field 'Post.approved' + db.delete_column('askbot_post', 'approved') + + # Deleting field 'Thread.approved' + db.delete_column('askbot_thread', 'approved') + + # Deleting field 'PostRevision.approved' + db.delete_column('askbot_postrevision', 'approved') + + # Deleting field 'PostRevision.approved_by' + db.delete_column('askbot_postrevision', 'approved_by_id') + + # Deleting field 'PostRevision.approved_at' + db.delete_column('askbot_postrevision', 'approved_at') + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.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.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + '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.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']"}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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': "orm['auth.User']", 'null': 'True', 'blank': '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot']
\ No newline at end of file diff --git a/askbot/migrations/0118_auto__add_field_postrevision_by_email.py b/askbot/migrations/0118_auto__add_field_postrevision_by_email.py new file mode 100644 index 00000000..7c304d1e --- /dev/null +++ b/askbot/migrations/0118_auto__add_field_postrevision_by_email.py @@ -0,0 +1,304 @@ +# -*- 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 'PostRevision.by_email' + db.add_column('askbot_postrevision', 'by_email', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + def backwards(self, orm): + # Deleting field 'PostRevision.by_email' + db.delete_column('askbot_postrevision', 'by_email') + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.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.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + '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.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'}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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': "orm['auth.User']", 'null': 'True', 'blank': '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot']
\ No newline at end of file diff --git a/askbot/migrations/0119_auto__add_postflagreason.py b/askbot/migrations/0119_auto__add_postflagreason.py new file mode 100644 index 00000000..1e2aedb8 --- /dev/null +++ b/askbot/migrations/0119_auto__add_postflagreason.py @@ -0,0 +1,317 @@ +# -*- 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 model 'PostFlagReason' + db.create_table('askbot_postflagreason', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('added_at', self.gf('django.db.models.fields.DateTimeField')()), + ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('title', self.gf('django.db.models.fields.CharField')(max_length=128)), + ('details', self.gf('django.db.models.fields.related.ForeignKey')(related_name='post_reject_reasons', to=orm['askbot.Post'])), + )) + db.send_create_signal('askbot', ['PostFlagReason']) + + def backwards(self, orm): + # Deleting model 'PostFlagReason' + db.delete_table('askbot_postflagreason') + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.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.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + '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'}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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': "orm['auth.User']", 'null': 'True', 'blank': '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot'] diff --git a/askbot/migrations/0120_auto__add_field_groupprofile_moderate_email__add_field_postrevision_em.py b/askbot/migrations/0120_auto__add_field_groupprofile_moderate_email__add_field_postrevision_em.py new file mode 100644 index 00000000..4b9efe58 --- /dev/null +++ b/askbot/migrations/0120_auto__add_field_groupprofile_moderate_email__add_field_postrevision_em.py @@ -0,0 +1,322 @@ +# -*- 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 'GroupProfile.moderate_email' + db.add_column('askbot_groupprofile', 'moderate_email', + self.gf('django.db.models.fields.BooleanField')(default=True), + keep_default=False) + + # Adding field 'PostRevision.email_address' + db.add_column('askbot_postrevision', 'email_address', + self.gf('django.db.models.fields.EmailField')(max_length=75, null=True, blank=True), + keep_default=False) + + def backwards(self, orm): + # Deleting field 'GroupProfile.moderate_email' + db.delete_column('askbot_groupprofile', 'moderate_email') + + # Deleting field 'PostRevision.email_address' + db.delete_column('askbot_postrevision', 'email_address') + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.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.groupmembership': { + 'Meta': {'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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': "orm['auth.User']", 'null': 'True', 'blank': '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot']
\ No newline at end of file diff --git a/askbot/migrations/0121_auto__add_field_groupprofile_is_open__add_field_groupprofile_preapprov.py b/askbot/migrations/0121_auto__add_field_groupprofile_is_open__add_field_groupprofile_preapprov.py new file mode 100644 index 00000000..e875198c --- /dev/null +++ b/askbot/migrations/0121_auto__add_field_groupprofile_is_open__add_field_groupprofile_preapprov.py @@ -0,0 +1,340 @@ +# -*- 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 'GroupProfile.is_open' + db.add_column('askbot_groupprofile', 'is_open', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + # Adding field 'GroupProfile.preapproved_emails' + db.add_column('askbot_groupprofile', 'preapproved_emails', + self.gf('django.db.models.fields.TextField')(default='', null=True, blank=True), + keep_default=False) + + # Adding field 'GroupProfile.preapproved_email_domains' + db.add_column('askbot_groupprofile', 'preapproved_email_domains', + self.gf('django.db.models.fields.TextField')(default='', null=True, blank=True), + keep_default=False) + + # Adding unique constraint on 'GroupMembership', fields ['group', 'user'] + db.create_unique('askbot_groupmembership', ['group_id', 'user_id']) + + def backwards(self, orm): + # Removing unique constraint on 'GroupMembership', fields ['group', 'user'] + db.delete_unique('askbot_groupmembership', ['group_id', 'user_id']) + + # Deleting field 'GroupProfile.is_open' + db.delete_column('askbot_groupprofile', 'is_open') + + # Deleting field 'GroupProfile.preapproved_emails' + db.delete_column('askbot_groupprofile', 'preapproved_emails') + + # Deleting field 'GroupProfile.preapproved_email_domains' + db.delete_column('askbot_groupprofile', 'preapproved_email_domains') + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.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.groupmembership': { + 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_open': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + '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'}) + }, + '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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': "orm['auth.User']", 'null': 'True', 'blank': '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot']
\ No newline at end of file diff --git a/askbot/migrations/0122_auth_user__add_subscribed_tag_field.py b/askbot/migrations/0122_auth_user__add_subscribed_tag_field.py new file mode 100644 index 00000000..d84666fb --- /dev/null +++ b/askbot/migrations/0122_auth_user__add_subscribed_tag_field.py @@ -0,0 +1,318 @@ +# -*- 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): + try: + # Adding field 'User.interesting_tags' + db.add_column(u'auth_user', 'subscribed_tags', self.gf('django.db.models.fields.TextField')(blank=True, default = ''), keep_default=False) + except: + pass + + def backwards(self, orm): + # Deleting field 'User.interesting_tags' + db.delete_column('auth_user', 'subscribed_tags') + + 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'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.anonymousquestion': { + 'Meta': {'object_name': 'AnonymousQuestion'}, + 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}), + 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'askbot.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.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.groupmembership': { + 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'GroupMembership'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_memberships'", 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_memberships'", 'to': "orm['auth.User']"}) + }, + 'askbot.groupprofile': { + 'Meta': {'object_name': 'GroupProfile'}, + 'group_tag': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'group_profile'", 'unique': 'True', 'to': "orm['askbot.Tag']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_open': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}), + 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + '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'}) + }, + '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']"}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_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']"}), + 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}), + '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', [], {}), + 'revision_type': ('django.db.models.fields.SmallIntegerField', [], {}), + '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', [], {}), + 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'}) + }, + '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.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'", 'to': "orm['askbot.Post']"}), + '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', [], {'unique': 'True', 'max_length': '255'}), + '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.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': "orm['auth.User']", 'null': 'True', 'blank': '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']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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']"}), + 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + '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.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.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), + 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), + 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), + 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['askbot'] diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 165808d9..3eca3c0e 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -26,15 +26,14 @@ from askbot.models.question import Thread from askbot.skins import utils as skin_utils from askbot.models.question import QuestionView, AnonymousQuestion from askbot.models.question import FavoriteQuestion -from askbot.models.answer import AnonymousAnswer from askbot.models.tag import Tag, MarkedTag -from askbot.models.meta import Vote from askbot.models.user import EmailFeedSetting, ActivityAuditStatus, Activity -from askbot.models.post import Post, PostRevision +from askbot.models.user import GroupMembership, GroupProfile +from askbot.models.post import Post, PostRevision, PostFlagReason, AnonymousAnswer from askbot.models.reply_by_email import ReplyAddress from askbot.models import signals from askbot.models.badges import award_badges_signal, get_badge, BadgeData -from askbot.models.repute import Award, Repute +from askbot.models.repute import Award, Repute, Vote from askbot import auth from askbot.utils.decorators import auto_now_timestamp from askbot.utils.slug import slugify @@ -43,8 +42,16 @@ from askbot.utils.url_utils import strip_path from askbot.utils import mail def get_model(model_name): + """a shortcut for getting model for an askbot app""" return models.get_model('askbot', model_name) +def get_admins_and_moderators(): + """returns query set of users who are site administrators + and moderators""" + return User.objects.filter( + models.Q(is_superuser=True) | models.Q(status='m') + ) + User.add_to_class( 'status', models.CharField( @@ -92,17 +99,18 @@ User.add_to_class('about', models.TextField(blank=True)) #interesting tags and ignored tags are to store wildcard tag selections only User.add_to_class('interesting_tags', models.TextField(blank = True)) User.add_to_class('ignored_tags', models.TextField(blank = True)) +User.add_to_class('subscribed_tags', models.TextField(blank = True)) User.add_to_class( 'email_tag_filter_strategy', models.SmallIntegerField( - choices=const.TAG_FILTER_STRATEGY_CHOICES, + choices=const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES, default=const.EXCLUDE_IGNORED ) ) User.add_to_class( 'display_tag_filter_strategy', models.SmallIntegerField( - choices=const.TAG_FILTER_STRATEGY_CHOICES, + choices=const.TAG_EMAIL_FILTER_STRATEGY_CHOICES, default=const.INCLUDE_ALL ) ) @@ -203,8 +211,12 @@ def user_has_affinity_to_question(self, question = None, affinity_type = None): affinity_type can be either "like" or "dislike" """ if affinity_type == 'like': - tag_selection_type = 'good' - wildcards = self.interesting_tags.split() + if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED: + tag_selection_type = 'subscribed' + wildcards = self.subscribed_tags.split() + else: + tag_selection_type = 'good' + wildcards = self.interesting_tags.split() elif affinity_type == 'dislike': tag_selection_type = 'bad' wildcards = self.ignored_tags.split() @@ -248,12 +260,17 @@ def user_has_interesting_wildcard_tags(self): and self.interesting_tags != '' ) - def user_can_have_strong_url(self): """True if user's homepage url can be followed by the search engine crawlers""" return (self.reputation >= askbot_settings.MIN_REP_TO_HAVE_STRONG_URL) +def user_can_reply_by_email(self): + """True, if reply by email is enabled + and user has sufficient reputatiton""" + return askbot_settings.REPLY_BY_EMAIL and \ + self.reputation > askbot_settings.MIN_REP_TO_POST_BY_EMAIL + def _assert_user_can( user = None, post = None, #related post (may be parent) @@ -314,6 +331,12 @@ def _assert_user_can( assert(error_message is not None) raise django_exceptions.PermissionDenied(error_message) +def user_assert_can_approve_post_revision(self, post_revision = None): + _assert_user_can( + user = self, + admin_or_moderator_required = True + ) + def user_assert_can_unaccept_best_answer(self, answer = None): assert getattr(answer, 'post_type', '') == 'answer' blocked_error_message = _( @@ -973,6 +996,7 @@ def user_post_comment( parent_post = None, body_text = None, timestamp = None, + by_email = False ): """post a comment on behalf of the user to parent_post @@ -991,9 +1015,11 @@ def user_post_comment( user = self, comment = body_text, added_at = timestamp, + by_email = by_email ) parent_post.thread.invalidate_cached_data() - award_badges_signal.send(None, + award_badges_signal.send( + None, event = 'post_comment', actor = self, context_object = comment, @@ -1001,6 +1027,23 @@ def user_post_comment( ) return comment +def user_post_tag_wiki( + self, + tag = None, + body_text = None, + timestamp = None + ): + """Creates a tag wiki post and assigns it + to the given tag. Returns the newly created post""" + tag_wiki_post = Post.objects.create_new_tag_wiki( + author = self, + text = body_text + ) + tag.tag_wiki = tag_wiki_post + tag.save() + return tag_wiki_post + + def user_post_anonymous_askbot_content(user, session_key): """posts any posts added just before logging in the posts are identified by the session key, thus the second argument @@ -1049,7 +1092,10 @@ def user_mark_tags( cleaned_wildcards = list() assert(action in ('add', 'remove')) if action == 'add': - assert(reason in ('good', 'bad')) + if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED: + assert(reason in ('good', 'bad', 'subscribed')) + else: + assert(reason in ('good', 'bad')) if wildcards: cleaned_wildcards = self.update_wildcard_tag_selections( action = action, @@ -1064,6 +1110,17 @@ def user_mark_tags( user = self, tag__name__in = tagnames ) + #Marks for "good" and "bad" reasons are exclusive, + #to make it impossible to "like" and "dislike" something at the same time + #but the subscribed set is independent - e.g. you can dislike a topic + #and still subscribe for it. + if reason == 'subscribed': + #don't touch good/bad marks + marked_ts = marked_ts.filter(reason = 'subscribed') + else: + #and in this case don't touch subscribed tags + marked_ts = marked_ts.exclude(reason = 'subscribed') + #todo: use the user api methods here instead of the straight ORM cleaned_tagnames = list() #those that were actually updated if action == 'remove': @@ -1085,7 +1142,8 @@ def user_mark_tags( cleaned_tagnames.extend(marked_names) cleaned_tagnames.extend(new_marks) else: - marked_ts.update(reason=reason) + if reason in ('good', 'bad'):#to maintain exclusivity of 'good' and 'bad' + marked_ts.update(reason=reason) cleaned_tagnames = tagnames return cleaned_tagnames, cleaned_wildcards @@ -1303,7 +1361,9 @@ def user_post_question( tags = None, wiki = False, is_anonymous = False, - timestamp = None + timestamp = None, + by_email = False, + email_address = None ): """makes an assertion whether user can post the question then posts it and returns the question object""" @@ -1320,6 +1380,8 @@ def user_post_question( if timestamp is None: timestamp = datetime.datetime.now() + #todo: split this into "create thread" + "add queston", if text exists + #or maybe just add a blank question post anyway thread = Thread.objects.create_new( author = self, title = title, @@ -1328,6 +1390,8 @@ def user_post_question( added_at = timestamp, wiki = wiki, is_anonymous = is_anonymous, + by_email = by_email, + email_address = email_address ) question = thread._question_post() if question.author != self: @@ -1336,42 +1400,70 @@ def user_post_question( # because they set some attributes for that instance and expect them to be changed also for question.author return question -def user_edit_comment(self, comment_post=None, body_text = None): +@auto_now_timestamp +def user_edit_comment( + self, + comment_post=None, + body_text = None, + timestamp = None, + by_email = False + ): """apply edit to a comment, the method does not change the comments timestamp and no signals are sent todo: see how this can be merged with edit_post todo: add timestamp """ self.assert_can_edit_comment(comment_post) - comment_post.text = body_text - comment_post.parse_and_save(author = self) - comment_post.thread.invalidate_cached_data() + comment_post.apply_edit( + text = body_text, + edited_at = timestamp, + edited_by = self, + by_email = by_email + ) def user_edit_post(self, post = None, body_text = None, revision_comment = None, - timestamp = None): + timestamp = None, + by_email = False + ): """a simple method that edits post body todo: unify it in the style of just a generic post this requires refactoring of underlying functions because we cannot bypass the permissions checks set within """ if post.post_type == 'comment': - self.edit_comment(comment_post = post, body_text = body_text) + self.edit_comment( + comment_post = post, + body_text = body_text, + by_email = by_email + ) elif post.post_type == 'answer': self.edit_answer( answer = post, body_text = body_text, timestamp = timestamp, - revision_comment = revision_comment + revision_comment = revision_comment, + by_email = by_email ) elif post.post_type == 'question': self.edit_question( question = post, body_text = body_text, timestamp = timestamp, - revision_comment = revision_comment + revision_comment = revision_comment, + by_email = by_email + ) + elif post.post_type == 'tag_wiki': + post.apply_edit( + edited_at = timestamp, + edited_by = self, + text = body_text, + #todo: summary name clash in question and question revision + comment = revision_comment, + wiki = True, + by_email = False ) else: raise NotImplementedError() @@ -1388,9 +1480,11 @@ def user_edit_question( edit_anonymously = False, timestamp = None, force = False,#if True - bypass the assert + by_email = False ): if force == False: self.assert_can_edit_question(question) + question.apply_edit( edited_at = timestamp, edited_by = self, @@ -1401,8 +1495,11 @@ def user_edit_question( tags = tags, wiki = wiki, edit_anonymously = edit_anonymously, + by_email = by_email ) + question.thread.invalidate_cached_data() + award_badges_signal.send(None, event = 'edit_question', actor = self, @@ -1418,7 +1515,8 @@ def user_edit_answer( revision_comment = None, wiki = False, timestamp = None, - force = False#if True - bypass the assert + force = False,#if True - bypass the assert + by_email = False ): if force == False: self.assert_can_edit_answer(answer) @@ -1428,6 +1526,7 @@ def user_edit_answer( text = body_text, comment = revision_comment, wiki = wiki, + by_email = by_email ) answer.thread.invalidate_cached_data() award_badges_signal.send(None, @@ -1437,13 +1536,56 @@ def user_edit_answer( timestamp = timestamp ) +@auto_now_timestamp +def user_create_post_reject_reason( + self, title = None, details = None, timestamp = None +): + """creates and returs the post reject reason""" + reason = PostFlagReason( + title = title, + added_at = timestamp, + author = self + ) + + #todo - need post_object.create_new() method + details = Post( + post_type = 'reject_reason', + author = self, + added_at = timestamp, + text = details + ) + details.parse_and_save(author = self) + details.add_revision( + author = self, + revised_at = timestamp, + text = details, + comment = const.POST_STATUS['default_version'] + ) + + reason.details = details + reason.save() + return reason + +@auto_now_timestamp +def user_edit_post_reject_reason( + self, reason, title = None, details = None, timestamp = None +): + reason.title = title + reason.save() + reason.details.apply_edit( + edited_by = self, + edited_at = timestamp, + text = details + ) + def user_post_answer( self, question = None, body_text = None, follow = False, wiki = False, - timestamp = None + timestamp = None, + by_email = False ): #todo: move this to assertion - user_assert_can_post_answer @@ -1455,6 +1597,7 @@ def user_post_answer( now = datetime.datetime.now() asked = question.added_at + #todo: this is an assertion, must be moved out if (now - asked < delta and self.reputation < askbot_settings.MIN_REP_TO_ANSWER_OWN_QUESTION): diff = asked + delta - now days = diff.days @@ -1505,7 +1648,8 @@ def user_post_answer( text = body_text, added_at = timestamp, email_notify = follow, - wiki = wiki + wiki = wiki, + by_email = by_email ) answer_post.thread.invalidate_cached_data() award_badges_signal.send(None, @@ -1821,20 +1965,26 @@ def user_get_tag_filtered_questions(self, questions = None): thread__tags__in = ignored_tags ).exclude( thread__tags__in = ignored_by_wildcards - ) + ).distinct() elif self.email_tag_filter_strategy == const.INCLUDE_INTERESTING: + if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED: + reason = 'subscribed' + wk = self.subscribed_tags.strip().split() + else: + reason = 'good' + wk = self.interesting_tags.strip().split() + selected_tags = Tag.objects.filter( - user_selections__reason = 'good', + user_selections__reason = reason, user_selections__user = self ) - wk = self.interesting_tags.strip().split() selected_by_wildcards = Tag.objects.get_by_wildcards(wk) tag_filter = models.Q(thread__tags__in = list(selected_tags)) \ | models.Q(thread__tags__in = list(selected_by_wildcards)) - return questions.filter( tag_filter ) + return questions.filter( tag_filter ).distinct() else: return questions @@ -2060,6 +2210,27 @@ def downvote(self, post, timestamp=None, cancel=False, force = False): ) @auto_now_timestamp +def user_approve_post_revision(user, post_revision, timestamp = None): + """approves the post revision and, if necessary, + the parent post and threads""" + user.assert_can_approve_post_revision() + + post_revision.approved = True + post_revision.approved_by = user + post_revision.approved_at = timestamp + + post_revision.save() + + post = post_revision.post + post.approved = True + post.save() + if post_revision.post.post_type == 'question': + thread = post.thread + thread.approved = True + thread.save() + post.thread.invalidate_cached_data() + +@auto_now_timestamp def flag_post(user, post, timestamp=None, cancel=False, cancel_all = False, force = False): if cancel_all: # remove all flags @@ -2148,33 +2319,62 @@ def user_update_wildcard_tag_selections( """updates the user selection of wildcard tags and saves the user object to the database """ + if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED: + assert reason in ('good', 'bad', 'subscribed') + else: + assert reason in ('good', 'bad') + new_tags = set(wildcards) interesting = set(self.interesting_tags.split()) ignored = set(self.ignored_tags.split()) + subscribed = set(self.subscribed_tags.split()) - target_set = interesting - other_set = ignored if reason == 'good': - pass + target_set = interesting + other_set = ignored elif reason == 'bad': target_set = ignored other_set = interesting + elif reason == 'subscribed': + target_set = subscribed + other_set = None else: assert(action == 'remove') if action == 'add': target_set.update(new_tags) - other_set.difference_update(new_tags) + if reason in ('good', 'bad'): + other_set.difference_update(new_tags) else: target_set.difference_update(new_tags) - other_set.difference_update(new_tags) + if reason in ('good', 'bad'): + other_set.difference_update(new_tags) self.interesting_tags = ' '.join(interesting) self.ignored_tags = ' '.join(ignored) + self.subscribed_tags = ' '.join(subscribed) self.save() return new_tags +def user_edit_group_membership(self, user = None, group = None, action = None): + """allows one user to add another to a group + or remove user from group. + + If when adding, the group does not exist, it will be created + the delete function is not symmetric, the group will remain + even if it becomes empty + """ + if action == 'add': + GroupMembership.objects.get_or_create(user = user, group = group) + elif action == 'remove': + GroupMembership.objects.get(user = user, group = group).delete() + else: + raise ValueError('invalid action') + +def user_is_group_member(self, group = None): + return self.group_memberships.filter(group = group).count() == 1 + User.add_to_class( 'add_missing_askbot_subscriptions', user_add_missing_askbot_subscriptions @@ -2209,7 +2409,10 @@ User.add_to_class( ) User.add_to_class('post_comment', user_post_comment) User.add_to_class('edit_comment', user_edit_comment) +User.add_to_class('create_post_reject_reason', user_create_post_reject_reason) +User.add_to_class('edit_post_reject_reason', user_edit_post_reject_reason) User.add_to_class('delete_post', user_delete_post) +User.add_to_class('post_tag_wiki', user_post_tag_wiki) User.add_to_class('visit_question', user_visit_question) User.add_to_class('upvote', upvote) User.add_to_class('downvote', downvote) @@ -2233,10 +2436,13 @@ User.add_to_class('is_following_question', user_is_following_question) User.add_to_class('mark_tags', user_mark_tags) User.add_to_class('update_response_counts', user_update_response_counts) User.add_to_class('can_have_strong_url', user_can_have_strong_url) +User.add_to_class('can_reply_by_email', user_can_reply_by_email) User.add_to_class('can_post_comment', user_can_post_comment) User.add_to_class('is_administrator', user_is_administrator) User.add_to_class('is_administrator_or_moderator', user_is_administrator_or_moderator) User.add_to_class('set_admin_status', user_set_admin_status) +User.add_to_class('edit_group_membership', user_edit_group_membership) +User.add_to_class('is_group_member', user_is_group_member) User.add_to_class('remove_admin_status', user_remove_admin_status) User.add_to_class('is_moderator', user_is_moderator) User.add_to_class('is_approved', user_is_approved) @@ -2265,6 +2471,7 @@ User.add_to_class( 'update_wildcard_tag_selections', user_update_wildcard_tag_selections ) +User.add_to_class('approve_post_revision', user_approve_post_revision) #assertions User.add_to_class('assert_can_vote_for_post', user_assert_can_vote_for_post) @@ -2293,9 +2500,13 @@ 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) User.add_to_class( - 'assert_can_unaccept_best_answer', - user_assert_can_unaccept_best_answer - ) + 'assert_can_unaccept_best_answer', + user_assert_can_unaccept_best_answer +) +User.add_to_class( + 'assert_can_approve_post_revision', + user_assert_can_approve_post_revision +) #todo: move this to askbot/utils ?? def format_instant_notification_email( @@ -2348,8 +2559,8 @@ def format_instant_notification_email( revisions = post.revisions.all()[:2] assert(len(revisions) == 2) content_preview = htmldiff( - revisions[1].as_html(), - revisions[0].as_html(), + revisions[1].html, + revisions[0].html, ins_start = '<b><u style="background-color:#cfc">', ins_end = '</u></b>', del_start = '<del style="color:#600;background-color:#fcc">', @@ -2357,34 +2568,62 @@ def format_instant_notification_email( ) #todo: remove hardcoded style else: - from askbot.templatetags.extra_filters_jinja import absolutize_urls_func - content_preview = absolutize_urls_func(post.html) - tag_style = "white-space: nowrap; " \ - + "font-size: 11px; color: #333;" \ - + "background-color: #EEE;" \ - + "border-left: 3px solid #777;" \ - + "border-top: 1px solid #EEE;" \ - + "border-bottom: 1px solid #CCC;" \ - + "border-right: 1px solid #CCC;" \ - + "padding: 1px 8px 1px 8px;" \ - + "margin-right:3px;" - if post.post_type == 'question':#add tags to the question - content_preview += '<div>' - for tag_name in post.get_tag_names(): - content_preview += '<span style="%s">%s</span>' % (tag_style, tag_name) - content_preview += '</div>' + content_preview = post.format_for_email() + + #add indented summaries for the parent posts + content_preview += post.format_for_email_as_parent_thread_summary() + + content_preview += '<p>======= Full thread summary =======</p>' + + content_preview += post.thread.format_for_email() + + if post.is_comment(): + if update_type.endswith('update'): + user_action = _('%(user)s edited a %(post_link)s.') + else: + user_action = _('%(user)s posted a %(post_link)s') + elif post.is_answer(): + if update_type.endswith('update'): + user_action = _('%(user)s edited an %(post_link)s.') + else: + user_action = _('%(user)s posted an %(post_link)s.') + elif post.is_question(): + if update_type.endswith('update'): + user_action = _('%(user)s edited a %(post_link)s.') + else: + user_action = _('%(user)s posted a %(post_link)s.') + else: + raise ValueError('unrecognized post type') + + post_url = strip_path(site_url) + post.get_absolute_url() + user_url = strip_path(site_url) + from_user.get_absolute_url() + user_action = user_action % { + 'user': '<a href="%s">%s</a>' % (user_url, from_user.username), + 'post_link': '<a href="%s">%s</a>' % (post_url, _(post.post_type)) + } + + can_reply = to_user.can_reply_by_email() + if can_reply: + reply_separator = const.REPLY_SEPARATOR_TEMPLATE % { + 'user_action': user_action, + 'instruction': _('To reply, PLEASE WRITE ABOVE THIS LINE.') + } + else: + reply_separator = user_action + update_data = { 'update_author_name': from_user.username, 'receiving_user_name': to_user.username, 'receiving_user_karma': to_user.reputation, 'reply_by_email_karma_threshold': askbot_settings.MIN_REP_TO_POST_BY_EMAIL, - 'can_reply': to_user.reputation > askbot_settings.MIN_REP_TO_POST_BY_EMAIL, + 'can_reply': can_reply, 'content_preview': content_preview,#post.get_snippet() 'update_type': update_type, - 'post_url': strip_path(site_url) + post.get_absolute_url(), + 'post_url': post_url, 'origin_post_title': origin_post.thread.title, 'user_subscriptions_url': user_subscriptions_url, + 'reply_separator': reply_separator } subject_line = _('"%(title)s"') % {'title': origin_post.thread.title} return subject_line, template.render(Context(update_data)) @@ -2400,6 +2639,8 @@ def send_instant_notifications_about_activity_in_post( newly mentioned users are carried through to reduce database hits """ + if askbot_settings.ENABLE_CONTENT_MODERATION and post.approved == False: + return if recipients is None: return @@ -2410,23 +2651,18 @@ def send_instant_notifications_about_activity_in_post( return from askbot.skins.loaders import get_template - template = get_template('instant_notification.html') - update_type_map = const.RESPONSE_ACTIVITY_TYPE_MAP_FOR_TEMPLATES update_type = update_type_map[update_activity.activity_type] origin_post = post.get_origin_post() for user in recipients: - - if askbot_settings.REPLY_BY_EMAIL: - template = get_template('instant_notification_reply_by_email.html') subject_line, body_text = format_instant_notification_email( to_user = user, from_user = update_activity.user, post = post, update_type = update_type, - template = template, + template = get_template('instant_notification.html') ) #todo: this could be packaged as an "action" - a bundle @@ -2450,9 +2686,6 @@ def send_instant_notifications_about_activity_in_post( headers = headers ) - - - #todo: move to utils def calculate_gravatar_hash(instance, **kwargs): """Calculates a User's gravatar hash from their email address.""" @@ -2473,7 +2706,15 @@ def record_post_update_activity( ): """called upon signal askbot.models.signals.post_updated which is sent at the end of save() method in posts + + this handler will set notifications about the post """ + if post.needs_moderation(): + #do not give notifications yet + #todo: it is possible here to trigger + #moderation email alerts + return + assert(timestamp != None) assert(updated_by != None) if newly_mentioned_users is None: @@ -2534,6 +2775,8 @@ def notify_award_message(instance, created, **kwargs): """ Notify users when they have been awarded badges by using Django message. """ + if askbot_settings.BADGES_MODE != 'public': + return if created: user = instance.user @@ -2661,10 +2904,7 @@ def record_flag_offensive(instance, mark_by, **kwargs): # recipients = instance.get_author_list( # exclude_list = [mark_by] # ) - recipients = User.objects.filter( - models.Q(is_superuser=True) | models.Q(status='m') - ) - activity.add_recipients(recipients) + activity.add_recipients(get_admins_and_moderators()) def remove_flag_offensive(instance, mark_by, **kwargs): "Remove flagging activity" @@ -2735,10 +2975,14 @@ def complete_pending_tag_subscriptions(sender, request, *args, **kwargs): """save pending tag subscriptions saved in the session""" if 'subscribe_for_tags' in request.session: (pure_tag_names, wildcards) = request.session.pop('subscribe_for_tags') + if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED: + reason = 'subscribed' + else: + reason = 'good' request.user.mark_tags( pure_tag_names, wildcards, - reason = 'good', + reason = reason, action = 'add' ) request.user.message_set.create( @@ -2771,7 +3015,6 @@ def set_user_avatar_type_flag(instance, created, **kwargs): def update_user_avatar_type_flag(instance, **kwargs): instance.user.update_avatar_type() - def make_admin_if_first_user(instance, **kwargs): """first user automatically becomes an administrator the function is run only once in the interpreter session @@ -2789,9 +3032,22 @@ def make_admin_if_first_user(instance, **kwargs): instance.set_admin_status() cache.cache.set('admin-created', True) +def place_post_revision_on_moderation_queue(instance, **kwargs): + """`instance` is post revision, because we must + be able to moderate all the revisions, if necessary, + in order to avoid people getting the post past the moderation + then make some evil edit. + """ + if instance.needs_moderation(): + instance.place_on_moderation_queue() + #signal for User model save changes django_signals.pre_save.connect(make_admin_if_first_user, sender=User) django_signals.pre_save.connect(calculate_gravatar_hash, sender=User) +django_signals.post_save.connect( + place_post_revision_on_moderation_queue, + sender=PostRevision +) django_signals.post_save.connect(add_missing_subscriptions, sender=User) django_signals.post_save.connect(record_award_event, sender=Award) django_signals.post_save.connect(notify_award_message, sender=Award) @@ -2840,6 +3096,7 @@ __all__ = [ 'Tag', 'Vote', + 'PostFlagReason', 'MarkedTag', 'BadgeData', @@ -2849,10 +3106,13 @@ __all__ = [ 'Activity', 'ActivityAuditStatus', 'EmailFeedSetting', + 'GroupMembership', + 'GroupProfile', 'User', 'ReplyAddress', - 'get_model' + 'get_model', + 'get_admins_and_moderators' ] diff --git a/askbot/models/answer.py b/askbot/models/answer.py deleted file mode 100644 index 8bed896f..00000000 --- a/askbot/models/answer.py +++ /dev/null @@ -1,19 +0,0 @@ -import datetime -from django.db import models -from askbot.models.base import AnonymousContent - - -class AnonymousAnswer(AnonymousContent): - question = models.ForeignKey('Post', related_name='anonymous_answers') - - def publish(self, user): - added_at = datetime.datetime.now() - from askbot import models - models.Post.objects.create_new_answer( - thread=self.question.thread, - author=user, - added_at=added_at, - wiki=self.wiki, - text=self.text - ) - self.delete() diff --git a/askbot/models/base.py b/askbot/models/base.py index 0686d50c..b3a12fbf 100644 --- a/askbot/models/base.py +++ b/askbot/models/base.py @@ -1,9 +1,7 @@ import datetime - from django.db import models from django.contrib.auth.models import User - class BaseQuerySetManager(models.Manager): """a base class that allows chainable qustom filters on the query sets @@ -44,9 +42,7 @@ class BaseQuerySetManager(models.Manager): class AnonymousContent(models.Model): - """ - Base class for AnonymousQuestion and AnonymousAnswer - """ + """Base class for AnonymousQuestion and AnonymousAnswer""" session_key = models.CharField(max_length=40) #session id for anonymous questions wiki = models.BooleanField(default=False) added_at = models.DateTimeField(default=datetime.datetime.now) diff --git a/askbot/models/meta.py b/askbot/models/meta.py deleted file mode 100644 index ff18b8be..00000000 --- a/askbot/models/meta.py +++ /dev/null @@ -1,81 +0,0 @@ -import datetime -from django.db import models - - -class VoteManager(models.Manager): - def get_up_vote_count_from_user(self, user): - if user is not None: - return self.filter(user=user, vote=1).count() - else: - return 0 - - def get_down_vote_count_from_user(self, user): - if user is not None: - return self.filter(user=user, vote=-1).count() - else: - return 0 - - def get_votes_count_today_from_user(self, user): - if user is not None: - today = datetime.date.today() - return self.filter(user=user, voted_at__range=(today, today + datetime.timedelta(1))).count() - else: - return 0 - - -class Vote(models.Model): - VOTE_UP = +1 - VOTE_DOWN = -1 - VOTE_CHOICES = ( - (VOTE_UP, u'Up'), - (VOTE_DOWN, u'Down'), - ) - user = models.ForeignKey('auth.User', related_name='votes') - voted_post = models.ForeignKey('Post', related_name='votes') - - vote = models.SmallIntegerField(choices=VOTE_CHOICES) - voted_at = models.DateTimeField(default=datetime.datetime.now) - - objects = VoteManager() - - class Meta: - unique_together = ('user', 'voted_post') - app_label = 'askbot' - db_table = u'vote' - - def __unicode__(self): - return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote) - - def __int__(self): - """1 if upvote -1 if downvote""" - return self.vote - - def is_upvote(self): - return self.vote == self.VOTE_UP - - def is_downvote(self): - return self.vote == self.VOTE_DOWN - - def is_opposite(self, vote_type): - assert(vote_type in (self.VOTE_UP, self.VOTE_DOWN)) - return self.vote != vote_type - - def cancel(self): - """cancel the vote - while taking into account whether vote was up - or down - - return change in score on the post - """ - #importing locally because of circular dependency - from askbot import auth - score_before = self.voted_post.score - if self.vote > 0: - # cancel upvote - auth.onUpVotedCanceled(self, self.voted_post, self.user) - else: - # cancel downvote - auth.onDownVotedCanceled(self, self.voted_post, self.user) - score_after = self.voted_post.score - - return score_after - score_before diff --git a/askbot/models/post.py b/askbot/models/post.py index 4ed528ea..daf7757b 100644 --- a/askbot/models/post.py +++ b/askbot/models/post.py @@ -13,6 +13,7 @@ from django.core import urlresolvers from django.db import models from django.utils import html as html_utils from django.utils.translation import ugettext as _ +from django.utils.translation import ungettext from django.utils.http import urlquote as django_urlquote from django.core import exceptions as django_exceptions from django.core.exceptions import ValidationError @@ -23,18 +24,17 @@ import askbot from askbot.utils.slug import slugify from askbot import const from askbot.models.user import EmailFeedSetting -from askbot.models.tag import MarkedTag, tags_match_some_wildcard +from askbot.models.tag import Tag, MarkedTag, tags_match_some_wildcard from askbot.conf import settings as askbot_settings from askbot import exceptions from askbot.utils import markup from askbot.utils.html import sanitize_html -from askbot.models.base import BaseQuerySetManager +from askbot.models.base import BaseQuerySetManager, AnonymousContent #todo: maybe merge askbot.utils.markup and forum.utils.html from askbot.utils.diff import textDiff as htmldiff from askbot.utils import mysql - class PostQuerySet(models.query.QuerySet): """ Custom query set subclass for :class:`~askbot.models.Post` @@ -148,44 +148,94 @@ class PostManager(BaseQuerySetManager): def get_comments(self): return self.filter(post_type='comment') - def create_new_answer(self, thread, author, added_at, text, wiki=False, email_notify=False): + def create_new_tag_wiki(self, text = None, author = None): + return self.create_new( + None,#this post type is threadless + author, + datetime.datetime.now(), + text, + wiki = True, + post_type = 'tag_wiki' + ) + + def create_new( + self, + thread, + author, + added_at, + text, + parent = None, + wiki = False, + email_notify = False, + post_type = None, + by_email = False + ): # TODO: Some of this code will go to Post.objects.create_new - answer = Post( - post_type='answer', - thread=thread, - author=author, - added_at=added_at, - wiki=wiki, - text=text, + + assert(post_type in const.POST_TYPES) + + post = Post( + post_type = post_type, + thread = thread, + parent = parent, + author = author, + added_at = added_at, + wiki = wiki, + text = text, #.html field is denormalized by the save() call ) - if answer.wiki: - answer.last_edited_by = answer.author - answer.last_edited_at = added_at - answer.wikified_at = added_at - answer.parse_and_save(author=author) + if post.wiki: + post.last_edited_by = post.author + post.last_edited_at = added_at + post.wikified_at = added_at + + post.parse_and_save(author=author) - answer.add_revision( - author=author, - revised_at=added_at, - text=text, + post.add_revision( + author = author, + revised_at = added_at, + text = text, comment = const.POST_STATUS['default_version'], + by_email = by_email ) - - #update thread data - thread.answer_count +=1 - thread.save() - thread.set_last_activity(last_activity_at=added_at, last_activity_by=author) # this should be here because it regenerates cached thread summary html - + + return post + + #todo: instead of this, have Thread.add_answer() + def create_new_answer( + self, + thread, + author, + added_at, + text, + wiki = False, + email_notify = False, + by_email = False + ): + answer = self.create_new( + thread, + author, + added_at, + text, + wiki = wiki, + post_type = 'answer', + by_email = by_email + ) #set notification/delete if email_notify: thread.followed_by.add(author) else: thread.followed_by.remove(author) + #update thread data + #todo: this totally belongs to some `Thread` class method + thread.answer_count += 1 + thread.save() + thread.set_last_activity(last_activity_at=added_at, last_activity_by=author) # this should be here because it regenerates cached thread summary html return answer + def precache_comments(self, for_posts, visitor): """ Fetches comments for given posts, and stores them in post._cached_comments @@ -237,11 +287,16 @@ class Post(models.Model): old_comment_id = models.PositiveIntegerField(null=True, blank=True, default=None, unique=True) parent = models.ForeignKey('Post', blank=True, null=True, related_name='comments') # Answer or Question for Comment - thread = models.ForeignKey('Thread', related_name='posts') + thread = models.ForeignKey('Thread', blank=True, null=True, default = None, related_name='posts') author = models.ForeignKey(User, related_name='posts') added_at = models.DateTimeField(default=datetime.datetime.now) + #denormalized data: the core approval of the posts is made + #in the revisions. In the revisions there is more data about + #approvals - by whom and when + approved = models.BooleanField(default=True, db_index=True) + deleted = models.BooleanField(default=False, db_index=True) deleted_at = models.DateTimeField(null=True, blank=True) deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_posts') @@ -301,7 +356,7 @@ class Post(models.Model): removed_mentions - list of mention <Activity> objects - for removed ones """ - if post.is_answer() or post.is_question(): + if post.post_type in ('question', 'answer', 'tag_wiki', 'reject_reason'): _urlize = False _use_markdown = True _escape_html = False #markdow does the escaping @@ -398,7 +453,7 @@ class Post(models.Model): #a hack allowing to save denormalized .summary field for questions if hasattr(post, 'summary'): - post.summary = strip_tags(post.html)[:120] + post.summary = post.get_snippet() #delete removed mentions for rm in removed_mentions: @@ -452,14 +507,25 @@ class Post(models.Model): def is_comment(self): return self.post_type == 'comment' + def is_tag_wiki(self): + return self.post_type == 'tag_wiki' + + def is_reject_reason(self): + return self.post_type == 'reject_reason' + + def needs_moderation(self): + return self.approved == False + def get_absolute_url(self, no_slug = False, question_post=None, thread=None): from askbot.utils.slug import slugify + #todo: the url generation function is pretty bad - + #the trailing slash is entered in three places here + in urls.py if not hasattr(self, '_thread_cache') and thread: self._thread_cache = thread if self.is_answer(): if not question_post: question_post = self.thread._question_post() - return u'%(base)s%(slug)s?answer=%(id)d#post-id-%(id)d' % { + return u'%(base)s%(slug)s/?answer=%(id)d#post-id-%(id)d' % { 'base': urlresolvers.reverse('question', args=[question_post.id]), 'slug': django_urlquote(slugify(self.thread.title)), 'id': self.id @@ -467,9 +533,9 @@ class Post(models.Model): elif self.is_question(): url = urlresolvers.reverse('question', args=[self.id]) if thread: - url += django_urlquote(slugify(thread.title)) + url += django_urlquote(slugify(thread.title)) + '/' elif no_slug is False: - url += django_urlquote(self.slug) + url += django_urlquote(self.slug) + '/' return url elif self.is_comment(): origin_post = self.get_origin_post() @@ -515,7 +581,7 @@ class Post(models.Model): def __unicode__(self): if self.is_question(): return self.thread.title - elif self.is_answer(): + elif self.is_answer() or self.is_reject_reason(): return self.html elif self.is_comment(): return self.text @@ -535,10 +601,98 @@ class Post(models.Model): return slugify(self.thread.title) slug = property(_get_slug) - def get_snippet(self): + def get_snippet(self, max_length = 120): """returns an abbreviated snippet of the content """ - return html_utils.strip_tags(self.html)[:120] + ' ...' + return html_utils.strip_tags(self.html)[:max_length] + ' ...' + + def format_tags_for_email(self): + """formats tags of the question post for email""" + tag_style = "white-space: nowrap; " \ + + "font-size: 11px; color: #333;" \ + + "background-color: #EEE;" \ + + "border-left: 3px solid #777;" \ + + "border-top: 1px solid #EEE;" \ + + "border-bottom: 1px solid #CCC;" \ + + "border-right: 1px solid #CCC;" \ + + "padding: 1px 8px 1px 8px;" \ + + "margin-right:3px;" + output = '<div>' + for tag_name in self.get_tag_names(): + output += '<span style="%s">%s</span>' % (tag_style, tag_name) + output += '</div>' + return output + + def format_for_email(self, quote_level = 0): + """format post for the output in email, + if quote_level > 0, the post will be indented that number of times + """ + from askbot.templatetags.extra_filters_jinja import absolutize_urls_func + output = '' + if self.post_type == 'question': + output += '<b>%s</b><br/>' % self.thread.title + + output += absolutize_urls_func(self.html) + if self.post_type == 'question':#add tags to the question + output += self.format_tags_for_email() + quote_style = 'padding-left:5px; border-left: 2px solid #aaa;' + while quote_level > 0: + quote_level = quote_level - 1 + output = '<div style="%s">%s</div>' % (quote_style, output) + return output + + def format_for_email_as_parent_thread_summary(self): + """format for email as summary of parent posts + all the way to the original question""" + quote_level = 0 + current_post = self + output = '' + while True: + parent_post = current_post.get_parent_post() + if parent_post is None: + break + quote_level += 1 + output += _( + 'In reply to %(user)s %(post)s of %(date)s<br/>' + ) % { + 'user': parent_post.author.username, + 'post': _(parent_post.post_type), + 'date': parent_post.added_at.strftime(const.DATETIME_FORMAT) + } + output += parent_post.format_for_email(quote_level = quote_level) + current_post = parent_post + return output + + def format_for_email_as_metadata(self): + output = _( + 'Posted by %(user)s on %(date)s' + ) % { + 'user': self.author.username, + 'date': self.added_at.strftime(const.DATETIME_FORMAT) + } + return '<p>%s</p>' % output + + def format_for_email_as_subthread(self): + """outputs question or answer and all it's comments + returns empty string for all other post types + """ + if self.post_type in ('question', 'answer'): + output = self.format_for_email_as_metadata() + output += self.format_for_email() + comments = self.get_cached_comments() + if comments: + comments_heading = ungettext( + '%(count)d comment:', + '%(count)d comments:', + len(comments) + ) % {'count': len(comments)} + output += '<p>%s</p>' % comments_heading + for comment in comments: + output += comment.format_for_email_as_metadata() + output += comment.format_for_email(quote_level = 1) + return output + else: + return '' def set_cached_comments(self, comments): """caches comments in the lifetime of the object @@ -553,22 +707,27 @@ class Post(models.Model): self._cached_comments = list() return self._cached_comments - def add_comment(self, comment=None, user=None, added_at=None): + def add_comment( + self, + comment=None, + user=None, + added_at=None, + by_email = False): + if added_at is None: added_at = datetime.datetime.now() - if None in (comment ,user): + if None in (comment, user): raise Exception('arguments comment and user are required') - from askbot.models import Post - comment = Post( - post_type='comment', - thread=self.thread, - parent=self, - text=comment, - author=user, - added_at=added_at - ) - comment.parse_and_save(author = user) + comment_post = self.__class__.objects.create_new( + self.thread, + user, + added_at, + comment, + parent = self, + post_type = 'comment', + by_email = by_email + ) self.comment_count = self.comment_count + 1 self.save() @@ -588,7 +747,7 @@ class Post(models.Model): # origin_post.last_activity_by = user # origin_post.save() - return comment + return comment_post def get_global_tag_based_subscribers( self, @@ -883,6 +1042,8 @@ class Post(models.Model): mentioned_users=mentioned_users, exclude_list=exclude_list ) + elif self.is_tag_wiki() or self.is_reject_reason(): + return list() raise NotImplementedError def get_latest_revision(self): @@ -965,9 +1126,21 @@ class Post(models.Model): def tagname_meta_generator(self): return u','.join([unicode(tag) for tag in self.get_tag_names()]) + def get_parent_post(self): + """returns parent post or None + if there is no parent, as it is in the case of question post""" + if self.post_type == 'comment': + return self.parent + elif self.post_type == 'answer': + return self.get_origin_post() + else: + return None + def get_origin_post(self): - if self.post_type == 'question': + if self.is_question(): return self + if self.is_tag_wiki() or self.is_reject_reason(): + return None else: return self.thread._question_post() @@ -1066,6 +1239,9 @@ class Post(models.Model): def _question__assert_is_visible_to(self, user): """raises QuestionHidden""" + if askbot_settings.ENABLE_CONTENT_MODERATION: + if self.approved == False: + raise exceptions.QuestionHidden() if self.deleted: message = _( 'Sorry, this question has been ' @@ -1150,14 +1326,33 @@ class Post(models.Model): return const.TYPE_ACTIVITY_COMMENT_QUESTION, self elif self.parent.post_type == 'answer': return const.TYPE_ACTIVITY_COMMENT_ANSWER, self + elif self.is_tag_wiki(): + if created: + return const.TYPE_ACTIVITY_CREATE_TAG_WIKI, self + else: + return const.TYPE_ACTIVITY_UPDATE_TAG_WIKI, self + elif self.is_reject_reason(): + if created: + return const.TYPE_ACTIVITY_CREATE_REJECT_REASON, self + else: + return const.TYPE_ACTIVITY_UPDATE_REJECT_REASON, self + raise NotImplementedError def get_tag_names(self): return self.thread.get_tag_names() - def _answer__apply_edit(self, edited_at=None, edited_by=None, text=None, comment=None, wiki=False): - + def __apply_edit( + self, + edited_at = None, + edited_by = None, + text = None, + comment = None, + wiki = False, + edit_anonymously = False, + by_email = False + ): if text is None: text = self.get_latest_revision().text if edited_at is None: @@ -1169,49 +1364,63 @@ class Post(models.Model): self.last_edited_by = edited_by #self.html is denormalized in save() self.text = text - #todo: bug wiki has no effect here + self.is_anonymous = edit_anonymously + + #wiki is an eternal trap whence there is no exit + if self.wiki == False and wiki == True: + self.wiki = True #must add revision before saving the answer self.add_revision( author = edited_by, revised_at = edited_at, text = text, - comment = comment + comment = comment, + by_email = by_email ) self.parse_and_save(author = edited_by) + def _answer__apply_edit( + self, + edited_at = None, + edited_by = None, + text = None, + comment = None, + wiki = False, + by_email = False + ): + + self.__apply_edit( + edited_at = edited_at, + edited_by = edited_by, + text = text, + comment = comment, + wiki = wiki, + by_email = by_email + ) + if edited_at is None: + edited_at = datetime.datetime.now() self.thread.set_last_activity(last_activity_at=edited_at, last_activity_by=edited_by) def _question__apply_edit(self, edited_at=None, edited_by=None, title=None,\ text=None, comment=None, tags=None, wiki=False,\ - edit_anonymously = False): + edit_anonymously = False, + by_email = False + ): + #todo: the thread editing should happen outside of this + #method, then we'll be able to unify all the *__apply_edit + #methods latest_revision = self.get_latest_revision() #a hack to allow partial edits - important for SE loader if title is None: title = self.thread.title - if text is None: - text = latest_revision.text if tags is None: tags = latest_revision.tagnames - - if edited_by is None: - raise Exception('parameter edited_by is required') - if edited_at is None: edited_at = datetime.datetime.now() - # Update the Question itself - self.last_edited_at = edited_at - self.last_edited_by = edited_by - self.text = text - self.is_anonymous = edit_anonymously - - #wiki is an eternal trap whence there is no exit - if self.wiki == False and wiki == True: - self.wiki = True - # Update the Question tag associations if latest_revision.tagnames != tags: self.thread.update_tags(tagnames = tags, user = edited_by, timestamp = edited_at) @@ -1220,27 +1429,39 @@ class Post(models.Model): self.thread.tagnames = tags self.thread.save() - # Create a new revision - self.add_revision( # has to be called AFTER updating the thread, otherwise it doesn't see new tags and the new title - author = edited_by, + self.__apply_edit( + edited_at = edited_at, + edited_by = edited_by, text = text, - revised_at = edited_at, - is_anonymous = edit_anonymously, comment = comment, + wiki = wiki, + edit_anonymously = edit_anonymously, + by_email = by_email ) - self.parse_and_save(author = edited_by) - self.thread.set_last_activity(last_activity_at=edited_at, last_activity_by=edited_by) - def apply_edit(self, *kargs, **kwargs): + def apply_edit(self, *args, **kwargs): + #todo: unify this, here we have unnecessary indirection + #the question__apply_edit function is backwards: + #the title edit and tag edit should apply to thread + #not the question post if self.is_answer(): - return self._answer__apply_edit(*kargs, **kwargs) + return self._answer__apply_edit(*args, **kwargs) elif self.is_question(): - return self._question__apply_edit(*kargs, **kwargs) + return self._question__apply_edit(*args, **kwargs) + elif self.is_tag_wiki() or self.is_comment() or self.is_reject_reason(): + return self.__apply_edit(*args, **kwargs) raise NotImplementedError - def _answer__add_revision(self, author=None, revised_at=None, text=None, comment=None): + def __add_revision( + self, + author = None, + revised_at = None, + text = None, + comment = None, + by_email = False + ): #todo: this may be identical to Question.add_revision if None in (author, revised_at, text): raise Exception('arguments author, revised_at and text are required') @@ -1252,12 +1473,13 @@ class Post(models.Model): comment = 'No.%s Revision' % rev_no from askbot.models.post import PostRevision return PostRevision.objects.create_answer_revision( - post=self, - author=author, - revised_at=revised_at, - text=text, - summary=comment, - revision=rev_no + post = self, + author = author, + revised_at = revised_at, + text = text, + summary = comment, + revision = rev_no, + by_email = by_email ) def _question__add_revision( @@ -1266,7 +1488,9 @@ class Post(models.Model): is_anonymous = False, text = None, comment = None, - revised_at = None + revised_at = None, + by_email = False, + email_address = None ): if None in (author, text): raise Exception('author, text and comment are required arguments') @@ -1287,12 +1511,15 @@ class Post(models.Model): revised_at = revised_at, tagnames = self.thread.tagnames, summary = comment, - text = text + text = text, + by_email = by_email, + email_address = email_address ) def add_revision(self, *kargs, **kwargs): - if self.is_answer(): - return self._answer__add_revision(*kargs, **kwargs) + #todo: unify these + if self.post_type in ('answer', 'comment', 'tag_wiki', 'reject_reason'): + return self.__add_revision(*kargs, **kwargs) elif self.is_question(): return self._question__add_revision(*kargs, **kwargs) raise NotImplementedError @@ -1365,12 +1592,15 @@ class Post(models.Model): def get_response_receivers(self, exclude_list = None): + """returns a list of response receiving users""" if self.is_answer(): return self._answer__get_response_receivers(exclude_list) elif self.is_question(): return self._question__get_response_receivers(exclude_list) elif self.is_comment(): return self._comment__get_response_receivers(exclude_list) + elif self.is_tag_wiki() or self.is_reject_reason(): + return list()#todo: who should get these? raise NotImplementedError def get_question_title(self): @@ -1418,7 +1648,7 @@ class Post(models.Model): return self.parent.comments.filter(added_at__lt = self.added_at).count() + 1 def is_upvoted_by(self, user): - from askbot.models.meta import Vote + from askbot.models.repute import Vote return Vote.objects.filter(user=user, voted_post=self, vote=Vote.VOTE_UP).exists() def is_last(self): @@ -1471,6 +1701,8 @@ class PostRevision(models.Model): post = models.ForeignKey('askbot.Post', related_name='revisions', null=True, blank=True) + #todo: remove this field, as revision type is determined by the + #Post.post_type revision_type is a useless field revision_type = models.SmallIntegerField(choices=REVISION_TYPE_CHOICES) # TODO: remove as we have Post now revision = models.PositiveIntegerField() @@ -1479,6 +1711,13 @@ class PostRevision(models.Model): summary = models.CharField(max_length=300, blank=True) text = models.TextField() + approved = models.BooleanField(default=False, db_index=True) + approved_by = models.ForeignKey(User, null = True, blank = True) + approved_at = models.DateTimeField(null = True, blank = True) + + by_email = models.BooleanField(default = False)#true, if edited by email + email_address = models.EmailField(null = True, blank = True) + # Question-specific fields title = models.CharField(max_length=300, blank=True, default='') tagnames = models.CharField(max_length=125, blank=True, default='') @@ -1494,6 +1733,92 @@ class PostRevision(models.Model): ordering = ('-revision',) app_label = 'askbot' + def needs_moderation(self): + """``True`` if post needs moderation""" + if askbot_settings.ENABLE_CONTENT_MODERATION: + #todo: needs a lot of details + if self.author.is_administrator_or_moderator(): + return False + if self.approved: + return False + + #if sent by email to group and group does not want moderation + if self.by_email and self.email: + group_name = self.email.split('@')[0] + try: + group = Tag.objects.get(name = group_name, deleted = False) + return group.group.profile.moderate_email + except Tag.DoesNotExist: + pass + return True + return False + + def place_on_moderation_queue(self): + """If revision is the first one, + keeps the post invisible until the revision + is aprroved. + If the revision is an edit, will autoapprove + but will still add it to the moderation queue. + + Eventually we might find a way to moderate every + edit as well.""" + #this is run on "post-save" so for a new post + #we'll have just one revision + if self.post.revisions.count() == 1: + activity_type = const.TYPE_ACTIVITY_MODERATED_NEW_POST + + self.approved = False + self.approved_by = None + self.approved_at = None + + self.post.approved = False + self.post.save() + + if self.post.is_question(): + self.post.thread.approved = False + self.post.thread.save() + #above changes will hide post from the public display + if self.by_email: + from askbot.utils.mail import send_mail + email_context = { + 'site': askbot_settings.APP_SHORT_NAME + } + body_text = _( + 'Thank you for your post to %(site)s. ' + 'It will be published after the moderators review.' + ) % email_context + send_mail( + subject_line = _('your post to %(site)s') % email_context, + body_text = body_text, + recipient_list = [self.author.email,], + ) + + else: + message = _( + 'Your post was placed on the moderation queue ' + 'and will be published after the moderator approval.' + ) + self.author.message_set.create(message = message) + else: + #In this case, for now we just flag the edit + #for the moderators. + #Ideally we'd need to hide the edit itself, + #but the complication is that when we have more + #than one edit in a row and then we'll need to deal with + #merging multiple edits. We don't have a solution for this yet. + activity_type = const.TYPE_ACTIVITY_MODERATED_POST_EDIT + + from askbot.models import Activity, get_admins_and_moderators + activity = Activity( + user = self.author, + content_object = self, + activity_type = activity_type, + question = self.get_origin_post() + ) + activity.save() + #todo: make this group-sensitive + activity.add_recipients(get_admins_and_moderators()) + def revision_type_str(self): return self.REVISION_TYPE_CHOICES_DICT[self.revision_type] @@ -1533,15 +1858,20 @@ class PostRevision(models.Model): @models.permalink def get_absolute_url(self): if self.is_question_revision(): - return 'question_revisions', (self.question.id,), {} + return 'question_revisions', (self.post.id,), {} elif self.is_answer_revision(): - return 'answer_revisions', (), {'id':self.answer.id} + return 'answer_revisions', (), {'id':self.post.id} def get_question_title(self): #INFO: ack-grepping shows that it's only used for Questions, so there's no code for Answers return self.question.thread.title - def as_html(self, **kwargs): + def get_origin_post(self): + """same as Post.get_origin_post()""" + return self.post.get_origin_post() + + @property + def html(self, **kwargs): markdowner = markup.get_parser() sanitized_html = sanitize_html(markdowner.convert(self.text)) @@ -1552,3 +1882,31 @@ class PostRevision(models.Model): } elif self.is_answer_revision(): return sanitized_html + + def get_snippet(self, max_length = 120): + """same as Post.get_snippet""" + return html_utils.strip_tags(self.html)[:max_length] + '...' + + +class PostFlagReason(models.Model): + added_at = models.DateTimeField() + author = models.ForeignKey('auth.User') + title = models.CharField(max_length=128) + details = models.ForeignKey(Post, related_name = 'post_reject_reasons') + class Meta: + app_label = 'askbot' + + +class AnonymousAnswer(AnonymousContent): + question = models.ForeignKey(Post, related_name='anonymous_answers') + + def publish(self, user): + added_at = datetime.datetime.now() + Post.objects.create_new_answer( + thread=self.question.thread, + author=user, + added_at=added_at, + wiki=self.wiki, + text=self.text + ) + self.delete() diff --git a/askbot/models/post_view.sql b/askbot/models/post_view.sql deleted file mode 100644 index c55b4027..00000000 --- a/askbot/models/post_view.sql +++ /dev/null @@ -1,255 +0,0 @@ -/* - -SQL for creating a database VIEW for the unmanaged `Post` model - -Tested with: SQLite3 - -Important: String literals should be wrapped in single quotes (http://www.sqlite.org/lang_keywords.html) - -*/ - --- DROP VIEW IF EXISTS askbot_post; - -CREATE VIEW askbot_post AS - -/* - -Answers - -*/ - -SELECT - answer.id + 1000000 AS id, -- fake unique ID - has to stay consistent with Post.parent_id for answer comments (defined below) ! - - /* Some required pseudo-fields */ - 'answer' AS post_type, - - NULL AS parent_id, - joined_question.thread_id AS thread_id, - - answer.id AS self_answer_id, - NULL AS self_question_id, - NULL AS self_comment_id, - - /* Shared fields from content.Content */ - answer.author_id, - answer.added_at, - - answer.deleted, - answer.deleted_at, - answer.deleted_by_id, - - answer.wiki, - answer.wikified_at, - - answer.locked, - answer.locked_by_id, - answer.locked_at, - - answer.score, - answer.vote_up_count, - answer.vote_down_count, - - answer.comment_count, - answer.offensive_flag_count, - - answer.last_edited_at, - answer.last_edited_by_id, - - answer.html, - answer.text, - - answer.summary, - - answer.is_anonymous - -FROM answer - -INNER JOIN question as joined_question -ON joined_question.id=answer.question_id - -UNION - -/* - -Questions - -*/ - -SELECT - question.id AS id, -- fake unique ID - has to stay consistent with Post.parent_id for question comments (defined below) ! - - /* Some required pseudo-fields */ - 'question' AS post_type, - - NULL AS parent_id, - question.thread_id, - - NULL AS self_answer_id, - question.id AS self_question_id, - NULL AS self_comment_id, - - /* Shared fields from content.Content */ - question.author_id, - question.added_at, - - question.deleted, - question.deleted_at, - question.deleted_by_id, - - question.wiki, - question.wikified_at, - - question.locked, - question.locked_by_id, - question.locked_at, - - question.score, - question.vote_up_count, - question.vote_down_count, - - question.comment_count, - question.offensive_flag_count, - - question.last_edited_at, - question.last_edited_by_id, - - question.html, - question.text, - - question.summary, - - question.is_anonymous - -FROM question - - -UNION - -/* - -Comments to Questions - -*/ - - -SELECT - comment.id + 2000000 AS id, -- fake unique ID - - /* Some required pseudo-fields */ - 'comment' AS post_type, - - joined_question.id AS parent_id, -- has to stay consistent with Post.is for joined_questions !! - joined_question.thread_id AS thread_id, - - NULL AS self_answer_id, - NULL AS self_question_id, - comment.id AS self_comment_id, - - /* Shared fields from content.Content */ - comment.user_id AS author_id, - comment.added_at, - - 0 AS deleted, - NULL AS deleted_at, - NULL AS deleted_by_id, - - 0 AS wiki, - NULL AS wikified_at, - - 0 AS locked, - NULL AS locked_by_id, - NULL AS locked_at, - - comment.score, - comment.score AS vote_up_count, - 0 AS vote_down_count, - - 0 AS comment_count, - comment.offensive_flag_count, - - NULL AS last_edited_at, - NULL AS last_edited_by_id, - - comment.html, - comment.comment AS text, - - '' AS summary, - - 0 AS is_anonymous - -FROM comment - -INNER JOIN django_content_type AS ct -ON ct.id=comment.content_type_id AND ct.app_label='askbot' AND ct.model='question' - -INNER JOIN question AS joined_question -ON joined_question.id=comment.object_id - - -UNION - -/* - -Comments to Answers - -*/ - - -SELECT - comment.id + 2000000 AS id, -- fake unique ID - - /* Some required pseudo-fields */ - 'comment' AS post_type, - - joined_answer.id + 1000000 AS parent_id, -- has to stay consistent with Post.is for joined_questions !! - joined_question.thread_id AS thread_id, - - NULL AS self_answer_id, - NULL AS self_question_id, - comment.id AS self_comment_id, - - /* Shared fields from content.Content */ - comment.user_id AS author_id, - comment.added_at, - - 0 AS deleted, - NULL AS deleted_at, - NULL AS deleted_by_id, - - 0 AS wiki, - NULL AS wikified_at, - - 0 AS locked, - NULL AS locked_by_id, - NULL AS locked_at, - - comment.score, - comment.score AS vote_up_count, - 0 AS vote_down_count, - - 0 AS comment_count, - comment.offensive_flag_count, - - NULL AS last_edited_at, - NULL AS last_edited_by_id, - - comment.html, - comment.comment AS text, - - '' AS summary, - - 0 AS is_anonymous - -FROM comment - -INNER JOIN django_content_type AS ct -ON ct.id=comment.content_type_id AND ct.app_label='askbot' AND ct.model='answer' - -INNER JOIN answer AS joined_answer -ON joined_answer.id=comment.object_id - -INNER JOIN question AS joined_question -ON joined_question.id=joined_answer.question_id - -; -- End of SQL statement diff --git a/askbot/models/question.py b/askbot/models/question.py index 79775afc..7d1c3758 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -9,6 +9,7 @@ from django.core import cache # import cache, not from cache import cache, to b from django.core.urlresolvers import reverse from django.utils.hashcompat import md5_constructor from django.utils.translation import ugettext as _ +from django.utils.translation import ungettext import askbot import askbot.conf @@ -61,7 +62,18 @@ class ThreadManager(models.Manager): def create(self, *args, **kwargs): raise NotImplementedError - def create_new(self, title, author, added_at, wiki, text, tagnames=None, is_anonymous=False): + def create_new( + self, + title, + author, + added_at, + wiki, + text, + tagnames = None, + is_anonymous = False, + by_email = False, + email_address = None + ): # TODO: Some of this code will go to Post.objects.create_new thread = super( @@ -74,6 +86,7 @@ class ThreadManager(models.Manager): last_activity_by=author ) + #todo: code below looks like ``Post.objects.create_new()`` question = Post( post_type='question', thread=thread, @@ -102,6 +115,8 @@ class ThreadManager(models.Manager): text = text, comment = const.POST_STATUS['default_version'], revised_at = added_at, + by_email = by_email, + email_address = email_address ) # INFO: Question has to be saved before update_tags() is called @@ -155,7 +170,13 @@ class ThreadManager(models.Manager): from askbot.conf import settings as askbot_settings # Avoid circular import # TODO: add a possibility to see deleted questions - qs = self.filter(posts__post_type='question', posts__deleted=False) # (***) brings `askbot_post` into the SQL query, see the ordering section below + qs = self.filter( + posts__post_type='question', + posts__deleted=False, + ) # (***) brings `askbot_post` into the SQL query, see the ordering section below + + if askbot_settings.ENABLE_CONTENT_MODERATION: + qs = qs.filter(approved = True) meta_data = {} @@ -169,8 +190,31 @@ class ThreadManager(models.Manager): qs = qs.filter(posts__post_type='question', posts__author__in=query_users) # TODO: unify with search_state.author ? tags = search_state.unified_tags() - for tag in tags: - qs = qs.filter(tags__name=tag) # Tags or AND-ed here, not OR-ed (i.e. we fetch only threads with all tags) + if len(tags) > 0: + + if askbot_settings.TAG_SEARCH_INPUT_ENABLED: + #todo: this may be gone or disabled per option + #"tag_search_box_enabled" + existing_tags = set( + Tag.objects.filter( + name__in = tags + ).values_list( + 'name', + flat = True + ) + ) + + non_existing_tags = set(tags) - existing_tags + meta_data['non_existing_tags'] = list(non_existing_tags) + tags = existing_tags + else: + meta_data['non_existing_tags'] = list() + + #construct filter for the tag search + for tag in tags: + qs = qs.filter(tags__name=tag) # Tags or AND-ed here, not OR-ed (i.e. we fetch only threads with all tags) + else: + meta_data['non_existing_tags'] = list() if search_state.scope == 'unanswered': qs = qs.filter(closed = False) # Do not show closed questions in unanswered section @@ -205,8 +249,19 @@ class ThreadManager(models.Manager): 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 - interesting_tags = Tag.objects.filter(user_selections__user=request_user, user_selections__reason='good') - ignored_tags = Tag.objects.filter(user_selections__user=request_user, user_selections__reason='bad') + interesting_tags = Tag.objects.filter( + user_selections__user = request_user, + user_selections__reason = 'good' + ) + ignored_tags = Tag.objects.filter( + user_selections__user = request_user, + user_selections__reason = 'bad' + ) + if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED: + meta_data['subscribed_tag_names'] = Tag.objects.filter( + user_selections__user = request_user, + user_selections__reason = 'subscribed' + ).values_list('name', flat = True) meta_data['interesting_tag_names'] = [tag.name for tag in interesting_tags] meta_data['ignored_tag_names'] = [tag.name for tag in ignored_tags] @@ -331,6 +386,11 @@ class Thread(models.Model): blank=True ) + #denormalized data: the core approval of the posts is made + #in the revisions. In the revisions there is more data about + #approvals - by whom and when + approved = models.BooleanField(default=True, db_index=True) + 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) @@ -416,6 +476,21 @@ class Thread(models.Model): else: return self.title + def format_for_email(self): + """experimental function: output entire thread for email""" + question, answers, junk = self.get_cached_post_data() + output = question.format_for_email_as_subthread() + if answers: + answer_heading = ungettext( + '%(count)d answer:', + '%(count)d answers:', + len(answers) + ) % {'count': len(answers)} + output += '<p>%s</p>' % answer_heading + for answer in answers: + output += answer.format_for_email_as_subthread() + return output + def tagname_meta_generator(self): return u','.join([unicode(tag) for tag in self.get_tag_names()]) @@ -456,7 +531,7 @@ class Thread(models.Model): #self.invalidate_cached_thread_content_fragment() self.update_summary_html() - def get_cached_post_data(self, sort_method = None): + def get_cached_post_data(self, sort_method = 'votes'): """returns cached post data, as calculated by the method get_post_data()""" key = self.get_post_data_cache_key(sort_method) @@ -466,7 +541,7 @@ class Thread(models.Model): cache.cache.set(key, post_data, const.LONG_TIME) return post_data - def get_post_data(self, sort_method = None): + def get_post_data(self, sort_method = 'votes'): """returns question, answers as list and a list of post ids for the given thread the returned posts are pre-stuffed with the comments @@ -475,9 +550,9 @@ class Thread(models.Model): """ thread_posts = self.posts.all().order_by( { - "latest":"-added_at", - "oldest":"added_at", - "votes":"-score" + 'latest':'-added_at', + 'oldest':'added_at', + 'votes':'-score' }[sort_method] ) #1) collect question, answer and comment posts and list of post id's @@ -490,6 +565,8 @@ class Thread(models.Model): #pass through only deleted question posts if post.deleted and post.post_type != 'question': continue + if post.approved == False:#hide posts on the moderation queue + continue post_to_author[post.id] = post.author_id @@ -633,7 +710,7 @@ class Thread(models.Model): previous_tags = list(self.tags.all()) previous_tagnames = set([tag.name for tag in previous_tags]) - updated_tagnames = set(t for t in tagnames.split(' ')) + updated_tagnames = set(t for t in tagnames.strip().split(' ')) removed_tagnames = previous_tagnames - updated_tagnames added_tagnames = updated_tagnames - previous_tagnames @@ -708,7 +785,7 @@ class Thread(models.Model): thread_question = self._question_post() - self.tagnames = tagnames + self.tagnames = tagnames.strip() self.save() # Update the Question itself diff --git a/askbot/models/reply_by_email.py b/askbot/models/reply_by_email.py index 26c53999..d329d38b 100644 --- a/askbot/models/reply_by_email.py +++ b/askbot/models/reply_by_email.py @@ -3,13 +3,14 @@ import random import string from django.db import models from django.contrib.auth.models import User -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext as _ from askbot.models.post import Post from askbot.models.base import BaseQuerySetManager from askbot.conf import settings as askbot_settings from askbot.utils import mail class ReplyAddressManager(BaseQuerySetManager): + """A manager for the :class:`ReplyAddress` model""" def get_unused(self, address, allowed_from_email): return self.get( @@ -19,6 +20,7 @@ class ReplyAddressManager(BaseQuerySetManager): ) def create_new(self, post, user): + """creates a new reply address""" reply_address = ReplyAddress( post = post, user = user, @@ -34,6 +36,8 @@ class ReplyAddressManager(BaseQuerySetManager): class ReplyAddress(models.Model): + """Stores a reply address for the post + and the user""" address = models.CharField(max_length = 25, unique = True) post = models.ForeignKey( Post, @@ -60,35 +64,53 @@ class ReplyAddress(models.Model): """True if was used""" return self.used_at != None - def edit_post(self, content, attachments = None): + def edit_post(self, parts): """edits the created post upon repeated response to the same address""" assert self.was_used == True - content += mail.process_attachments(attachments) + content, stored_files = mail.process_parts(parts) self.user.edit_post( post = self.response_post, body_text = content, - revision_comment = _('edited by email') + revision_comment = _('edited by email'), + by_email = True ) self.response_post.thread.invalidate_cached_data() - def create_reply(self, content, attachments = None): + def create_reply(self, parts): """creates a reply to the post which was emailed to the user """ result = None - content += mail.process_attachments(attachments) + #todo: delete stored files if this function fails + content, stored_files = mail.process_parts(parts) if self.post.post_type == 'answer': - result = self.user.post_comment(self.post, content) + result = self.user.post_comment( + self.post, + content, + by_email = True + ) elif self.post.post_type == 'question': wordcount = len(content)/6#this is a simplistic hack if wordcount > askbot_settings.MIN_WORDS_FOR_ANSWER_BY_EMAIL: - result = self.user.post_answer(self.post, content) + result = self.user.post_answer( + self.post, + content, + by_email = True + ) else: - result = self.user.post_comment(self.post, content) + result = self.user.post_comment( + self.post, + content, + by_email = True + ) elif self.post.post_type == 'comment': - result = self.user.post_comment(self.post.parent, content) + result = self.user.post_comment( + self.post.parent, + content, + by_email = True + ) result.thread.invalidate_cached_data() self.response_post = result self.used_at = datetime.now() diff --git a/askbot/models/repute.py b/askbot/models/repute.py index c9c1b8bf..09e74067 100644 --- a/askbot/models/repute.py +++ b/askbot/models/repute.py @@ -7,6 +7,85 @@ import datetime from askbot import const from django.core.urlresolvers import reverse +class VoteManager(models.Manager): + def get_up_vote_count_from_user(self, user): + if user is not None: + return self.filter(user=user, vote=1).count() + else: + return 0 + + def get_down_vote_count_from_user(self, user): + if user is not None: + return self.filter(user=user, vote=-1).count() + else: + return 0 + + def get_votes_count_today_from_user(self, user): + if user is not None: + today = datetime.date.today() + return self.filter(user=user, voted_at__range=(today, today + datetime.timedelta(1))).count() + else: + return 0 + + +class Vote(models.Model): + VOTE_UP = +1 + VOTE_DOWN = -1 + VOTE_CHOICES = ( + (VOTE_UP, u'Up'), + (VOTE_DOWN, u'Down'), + ) + user = models.ForeignKey('auth.User', related_name='votes') + voted_post = models.ForeignKey('Post', related_name='votes') + + vote = models.SmallIntegerField(choices=VOTE_CHOICES) + voted_at = models.DateTimeField(default=datetime.datetime.now) + + objects = VoteManager() + + class Meta: + unique_together = ('user', 'voted_post') + app_label = 'askbot' + db_table = u'vote' + + def __unicode__(self): + return '[%s] voted at %s: %s' %(self.user, self.voted_at, self.vote) + + def __int__(self): + """1 if upvote -1 if downvote""" + return self.vote + + def is_upvote(self): + return self.vote == self.VOTE_UP + + def is_downvote(self): + return self.vote == self.VOTE_DOWN + + def is_opposite(self, vote_type): + assert(vote_type in (self.VOTE_UP, self.VOTE_DOWN)) + return self.vote != vote_type + + def cancel(self): + """cancel the vote + while taking into account whether vote was up + or down + + return change in score on the post + """ + #importing locally because of circular dependency + from askbot import auth + score_before = self.voted_post.score + if self.vote > 0: + # cancel upvote + auth.onUpVotedCanceled(self, self.voted_post, self.user) + else: + # cancel downvote + auth.onDownVotedCanceled(self, self.voted_post, self.user) + score_after = self.voted_post.score + + return score_after - score_before + + class BadgeData(models.Model): """Awarded for notable actions performed on the site by Users.""" slug = models.SlugField(max_length=50, unique=True) @@ -166,3 +245,4 @@ class Repute(models.Model): 'question_title': self.question.thread.title, 'link_title': link_title } + diff --git a/askbot/models/tag.py b/askbot/models/tag.py index a13de661..779c0b68 100644 --- a/askbot/models/tag.py +++ b/askbot/models/tag.py @@ -82,6 +82,53 @@ class TagManager(BaseQuerySetManager): def get_query_set(self): return TagQuerySet(self.model) +#todo: implement this +#class GroupTagQuerySet(models.query.QuerySet): +# """Custom query set for the group""" +# def __init__(self, model): +def clean_group_name(name): + """group names allow spaces, + tag names do not, so we use this method + to replace spaces with dashes""" + return re.sub('\s+', '-', name.strip()) + +class GroupTagManager(TagManager): + """manager for group tags""" + +# def get_query_set(self): +# return GroupTagQuerySet(self.model) + + def get_or_create(self, group_name = None, user = None): + """creates a group tag or finds one, if exists""" + #todo: here we might fill out the group profile + + #replace spaces with dashes + group_name = clean_group_name(group_name) + try: + tag = self.get(name = group_name) + except self.model.DoesNotExist: + tag = self.model(name = group_name, created_by = user) + tag.save() + from askbot.models.user import GroupProfile + group_profile = GroupProfile(group_tag = tag) + group_profile.save() + return tag + + #todo: maybe move this to query set + def get_for_user(self, user = None): + return self.filter(user_memberships__user = user) + + #todo: remove this when the custom query set is done + def get_all(self): + return self.annotate( + member_count = models.Count('user_memberships') + ).filter( + member_count__gt = 0 + ) + + def get_by_name(self, group_name = None): + return self.get(name = clean_group_name(group_name)) + class Tag(models.Model): name = models.CharField(max_length=255, unique=True) created_by = models.ForeignKey(User, related_name='created_tags') @@ -92,7 +139,14 @@ class Tag(models.Model): deleted_at = models.DateTimeField(null=True, blank=True) deleted_by = models.ForeignKey(User, null=True, blank=True, related_name='deleted_tags') + tag_wiki = models.OneToOneField( + 'Post', + null=True, + related_name = 'described_tag' + ) + objects = TagManager() + group_tags = GroupTagManager() class Meta: app_label = 'askbot' @@ -103,7 +157,11 @@ class Tag(models.Model): return self.name class MarkedTag(models.Model): - TAG_MARK_REASONS = (('good', _('interesting')), ('bad', _('ignored'))) + TAG_MARK_REASONS = ( + ('good', _('interesting')), + ('bad', _('ignored')), + ('subscribed', _('subscribed')), + ) tag = models.ForeignKey('Tag', related_name='user_selections') user = models.ForeignKey(User, related_name='tag_selections') reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS) diff --git a/askbot/models/user.py b/askbot/models/user.py index 6f27cbf3..70aee263 100644 --- a/askbot/models/user.py +++ b/askbot/models/user.py @@ -1,14 +1,19 @@ import datetime import logging +import re from django.db import models from django.db.backends.dummy.base import IntegrityError from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.contrib.auth.models import User +from django.core import exceptions +from django.forms import EmailField, URLField from django.utils.translation import ugettext as _ from django.utils.html import strip_tags from askbot import const from askbot.utils import functions +from askbot.models.tag import Tag +from askbot.forms import DomainNameField class ResponseAndMentionActivityManager(models.Manager): def get_query_set(self): @@ -199,9 +204,9 @@ class Activity(models.Model): assert(user_count == 1) return user_qs[0] - def get_preview(self): - if self.summary == '': - return strip_tags(self.content_object.html)[:300] + def get_snippet(self, max_length = 120): + if self.summary == '': + return self.content_object.get_snippet(max_length) else: return self.summary @@ -291,6 +296,8 @@ class EmailFeedSetting(models.Model): class Meta: #added to make account merges work properly unique_together = ('subscriber', 'feed_type') + app_label = 'askbot' + def __str__(self): if self.reported_at is None: @@ -331,5 +338,89 @@ class EmailFeedSetting(models.Model): self.reported_at = datetime.datetime.now() self.save() + +class GroupMembership(models.Model): + """an explicit model to link users and the tags + that by being recorded with this relation automatically + become group tags + """ + group = models.ForeignKey(Tag, related_name = 'user_memberships') + user = models.ForeignKey(User, related_name = 'group_memberships') + + class Meta: + app_label = 'askbot' + unique_together = ('group', 'user') + +class GroupProfile(models.Model): + """stores group profile data""" + group_tag = models.OneToOneField( + Tag, + unique = True, + related_name = 'group_profile' + ) + logo_url = models.URLField(null = True) + moderate_email = models.BooleanField(default = True) + is_open = models.BooleanField(default = False) + #preapproved email addresses and domain names to auto-join groups + #trick - the field is padded with space and all tokens are space separated + preapproved_emails = models.TextField( + null = True, blank = True, default = '' + ) + #only domains - without the '@' or anything before them + preapproved_email_domains = models.TextField( + null = True, blank = True, default = '' + ) + class Meta: app_label = 'askbot' + + def can_accept_user(self, user): + """True if user is preapproved to join the group""" + if user.is_anonymous(): + return False + + if self.is_open: + return True + + if user.is_administrator_or_moderator(): + return True + + #relying on a specific method of storage + if self.preapproved_emails: + email_match_re = re.compile(r'\s%s\s' % user.email) + if email_match_re.search(self.preapproved_emails): + return True + + if self.preapproved_email_domains: + email_domain = user.email.split('@')[1] + domain_match_re = re.compile(r'\s%s\s' % email_domain) + return domain_match_re.search(self.preapproved_email_domains) + + return False + + def clean(self): + """called in `save()` + """ + emails = functions.split_list(self.preapproved_emails) + email_field = EmailField() + try: + map(lambda v: email_field.clean(v), emails) + except exceptions.ValidationError: + raise exceptions.ValidationError( + _('Please give a list of valid email addresses.') + ) + self.preapproved_emails = ' ' + '\n'.join(emails) + ' ' + + domains = functions.split_list(self.preapproved_email_domains) + domain_field = DomainNameField() + try: + map(lambda v: domain_field.clean(v), domains) + except exceptions.ValidationError: + raise exceptions.ValidationError( + _('Please give a list of valid email domain names.') + ) + self.preapproved_email_domains = ' ' + '\n'.join(domains) + ' ' + + def save(self, *args, **kwargs): + self.clean() + super(GroupProfile, self).save(*args, **kwargs) diff --git a/askbot/search/state_manager.py b/askbot/search/state_manager.py index 8096cbdd..f8154865 100644 --- a/askbot/search/state_manager.py +++ b/askbot/search/state_manager.py @@ -216,9 +216,14 @@ class SearchState(object): ss.page = 1 return ss - def remove_tags(self): + def remove_tags(self, tags = None): ss = self.deepcopy() - ss.tags = [] + if tags: + ss.tags = list( + set(ss.tags) - set(tags) + ) + else: + ss.tags = [] ss.page = 1 return ss diff --git a/askbot/setup_templates/settings.py b/askbot/setup_templates/settings.py index 104ea64b..632c4e70 100644 --- a/askbot/setup_templates/settings.py +++ b/askbot/setup_templates/settings.py @@ -98,7 +98,7 @@ TEMPLATE_LOADERS = ( MIDDLEWARE_CLASSES = ( #'django.middleware.gzip.GZipMiddleware', - 'askbot.middleware.locale.LocaleMiddleware', + #'askbot.middleware.locale.LocaleMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', #'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', diff --git a/askbot/skins/common/media/jquery-openid/openid.css b/askbot/skins/common/media/jquery-openid/openid.css index f9430108..9a1db85f 100644 --- a/askbot/skins/common/media/jquery-openid/openid.css +++ b/askbot/skins/common/media/jquery-openid/openid.css @@ -1,9 +1,8 @@ -div#login-icons {padding:20px 0 0 0;} +div#login-icons {padding: 0;} ul.login-icons {width: 450px; margin:0;padding:0;text-align:left; list-style-type:none; display:block;} ul.login-icons li {display:inline;} ul.large input {height: 40px; width: 90px;border:1px solid #ccc;margin:0 5px 5px 0;} -.openid-signin h1 {margin-top: -15px; padding-bottom: 10px;} -.openid-signin h2 {margin-top:15px;} +.openid-signin h1 {padding-bottom: 10px;} .openid-signin h2#account-recovery-heading {margin-bottom:2px;} #account-recovery-form p.hint a {color:#1b79bd; text-decoration: none;} #account-recovery-form p.hint a:hover {text-decoration: underline;} diff --git a/askbot/skins/common/media/js/live_search.js b/askbot/skins/common/media/js/live_search.js index 100c3f67..f33862a1 100644 --- a/askbot/skins/common/media/js/live_search.js +++ b/askbot/skins/common/media/js/live_search.js @@ -1,3 +1,55 @@ +var TagWarningBox = function(){ + WrappedElement.call(this); + this._tags = []; +}; +inherits(TagWarningBox, WrappedElement); + +TagWarningBox.prototype.createDom = function(){ + this._element = this.makeElement('div'); + this._element + .css('display', 'block') + .css('margin', '0 0 13px 2px'); + this._element.addClass('non-existing-tags'); + this._warning = this.makeElement('p'); + this._element.append(this._warning); + this._tag_container = this.makeElement('ul'); + this._tag_container.addClass('tags'); + this._element.append(this._tag_container); + this._element.append($('<div class="clearfix"></div>')); + this._element.hide(); +}; + +TagWarningBox.prototype.clear = function(){ + this._tags = []; + if (this._tag_container){ + this._tag_container.empty(); + } + this._warning.hide(); + this._element.hide(); +}; + +TagWarningBox.prototype.addTag = function(tag_name){ + var tag = new Tag(); + tag.setName(tag_name); + tag.setLinkable(false); + tag.setDeletable(false); + var elem = this.getElement(); + this._tag_container.append(tag.getElement()); + this._tag_container.css('display', 'block'); + this._tags.push(tag); + elem.show(); +}; + +TagWarningBox.prototype.showWarning = function(){ + this._warning.html( + ngettext( + 'Sorry, this tag does not exist', + 'Sorry, these tags do not exist', + this._tags.length + ) + ); + this._warning.show(); +}; var liveSearch = function(query_string) { var query = $('input#keywords'); @@ -7,6 +59,70 @@ var liveSearch = function(query_string) { var q_list_sel = 'question-list';//id of question listing div var search_url = askbot['urls']['questions']; var x_button = $('input[name=reset_query]'); + var tag_warning_box = new TagWarningBox(); + + //the tag search input is optional in askbot + $('#ab-tag-search').parent().before( + tag_warning_box.getElement() + ); + + var run_tag_search = function(){ + var search_tags = $('#ab-tag-search').val().split(/\s+/); + if (search_tags.length === 0) { + return; + } + /** @todo: the questions/ might need translation... */ + query_string = '/questions/scope:all/sort:activity-desc/page:1/' + $.each(search_tags, function(idx, tag) { + query_string = QSutils.add_search_tag(query_string, search_tags); + }); + var url = search_url + query_string; + $.ajax({ + url: url, + dataType: 'json', + success: function(data, text_status, xhr){ + render_result(data, text_status, xhr); + $('#ab-tag-search').val(''); + }, + }); + updateHistory(url); + }; + + var activate_tag_search_input = function(){ + //the autocomplete is set up in tag_selector.js + var button = $('#ab-tag-search-add'); + if (button.length === 0){//may be absent + return; + } + var ac = new AutoCompleter({ + url: askbot['urls']['get_tag_list'], + preloadData: true, + minChars: 1, + useCache: true, + matchInside: true, + maxCacheLength: 100, + maxItemsToShow: 20, + onItemSelect: run_tag_search, + delay: 10 + }); + ac.decorate($('#ab-tag-search')); + setupButtonEventHandlers(button, run_tag_search); + //var tag_search_input = $('#ab-tag-search'); + //tag_search_input.keydown( + // makeKeyHandler(13, run_tag_search) + //); + }; + + var render_tag_warning = function(tag_list){ + if ( !tag_list ) { + return; + } + tag_warning_box.clear(); + $.each(tag_list, function(idx, tag_name){ + tag_warning_box.addTag(tag_name); + }); + tag_warning_box.showWarning(); + }; var refresh_x_button = function(){ if(query_val().length > 0){ @@ -69,6 +185,10 @@ var liveSearch = function(query_string) { }, cache: false }); + updateHistory(url); + }; + + var updateHistory = function(url) { var context = { state:1, rand:Math.random() }; History.pushState( context, "Questions", url ); setTimeout(function (){ @@ -195,6 +315,7 @@ var liveSearch = function(query_string) { } render_related_tags(data['related_tags'], data['query_string']); render_relevance_sort_tab(data['query_string']); + render_tag_warning(data['non_existing_tags']); set_active_sort_tab(data['query_data']['sort_order'], data['query_string']); if(data['feed_url']){ // Change RSS URL @@ -214,11 +335,11 @@ var liveSearch = function(query_string) { var askHrefBase = askButton.attr('href').split('?')[0]; askButton.attr('href', askHrefBase + data['query_data']['ask_query_string']); /* INFO: ask_query_string should already be URL-encoded! */ - query.focus(); var old_list = $('#' + q_list_sel); var new_list = $('<div></div>').hide().html(data['questions']); + new_list.find('.timeago').timeago(); old_list.stop(true).after(new_list).fadeOut(200, function() { //show new div with a fadeIn effect old_list.remove(); @@ -260,6 +381,8 @@ var liveSearch = function(query_string) { } }); + activate_tag_search_input(); + $("form#searchForm").submit(function(event) { // if user clicks the button the s(h)e probably wants page reload, // so provide that experience but first update the query string diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js index 62818093..0f349fd8 100644 --- a/askbot/skins/common/media/js/post.js +++ b/askbot/skins/common/media/js/post.js @@ -1542,9 +1542,11 @@ Comment.prototype.setContent = function(data){ this._comment_body.append(this._user_link); this._comment_body.append(' ('); - this._comment_age = $('<span class="age"></span>'); - this._comment_age.html(this._data['comment_age']); - this._comment_body.append(this._comment_age); + this._comment_added_at = $('<abbr class="timeago"></abbr>'); + this._comment_added_at.html(this._data['comment_added_at']); + this._comment_added_at.attr('title', this._data['comment_added_at']); + this._comment_added_at.timeago(); + this._comment_body.append(this._comment_added_at); this._comment_body.append(')'); if (this._editable){ @@ -1567,8 +1569,8 @@ Comment.prototype.dispose = function(){ if (this._user_link){ this._user_link.remove(); } - if (this._comment_age){ - this._comment_age.remove(); + if (this._comment_added_at){ + this._comment_added_at.remove(); } if (this._delete_icon){ this._delete_icon.dispose(); @@ -1906,6 +1908,481 @@ QASwapper.prototype.startSwapping = function(){ } }; +/** + * @constructor + */ +var WMD = function(){ + WrappedElement.call(this); + this._markdown = undefined; + this._enabled_buttons = 'bold italic link blockquote code ' + + 'image attachment ol ul heading hr'; + this._is_previewer_enabled = true; +}; +inherits(WMD, WrappedElement); + +WMD.prototype.setEnabledButtons = function(buttons){ + this._enabled_buttons = buttons; +}; + +WMD.prototype.setPreviewerEnabled = function(state){ + this._is_previewer_enabled = state; + if (this._previewer){ + this._previewer.hide(); + } +}; + +WMD.prototype.setEscapeHandler = function(handler){ + this._escape_handler = handler; +}; + +WMD.prototype.createDom = function(){ + this._element = this.makeElement('div'); + var clearfix = this.makeElement('div').addClass('clearfix'); + this._element.append(clearfix); + + var wmd_container = this.makeElement('div'); + wmd_container.addClass('wmd-container'); + this._element.append(wmd_container); + + var wmd_buttons = this.makeElement('div') + .attr('id', 'wmd-button-bar') + .addClass('wmd-panel'); + wmd_container.append(wmd_buttons); + + var editor = this.makeElement('textarea') + .attr('id', 'editor'); + wmd_container.append(editor); + this._textarea = editor; + + if (this._markdown){ + editor.val(this._markdown); + } + + var previewer = this.makeElement('div') + .attr('id', 'previewer') + .addClass('wmd-preview'); + wmd_container.append(previewer); + this._previewer = previewer; + if (this._is_previewer_enabled === false) { + previewer.hide(); + } +}; + +WMD.prototype.setMarkdown = function(text){ + this._markdown = text; + if (this._textarea){ + this._textarea.val(text); + } +}; + +WMD.prototype.getMarkdown = function(){ + return this._textarea.val(); +}; + +WMD.prototype.start = function(){ + Attacklab.Util.startEditor(true, this._enabled_buttons); + this._textarea.keyup(makeKeyHandler(27, this._escape_handler)); +}; + +/** + * @constructor + */ +var TagWikiEditor = function(){ + WrappedElement.call(this); + this._state = 'display';//'edit' or 'display' + this._content_backup = ''; + this._is_editor_loaded = false; + this._enabled_editor_buttons = null; + this._is_previewer_enabled = false; +}; +inherits(TagWikiEditor, WrappedElement); + +TagWikiEditor.prototype.backupContent = function(){ + this._content_backup = this._content_box.contents(); +}; + +TagWikiEditor.prototype.setEnabledEditorButtons = function(buttons){ + this._enabled_editor_buttons = buttons; +}; + +TagWikiEditor.prototype.setPreviewerEnabled = function(state){ + this._is_previewer_enabled = state; + if (this.isEditorLoaded()){ + this._editor.setPreviewerEnabled(this._is_previewer_enabled); + } +}; + +TagWikiEditor.prototype.setContent = function(content){ + this._content_box.empty(); + this._content_box.append(content); +}; + +TagWikiEditor.prototype.setState = function(state){ + if (state === 'edit'){ + this._state = state; + this._edit_btn.hide(); + this._cancel_btn.show(); + this._save_btn.show(); + this._cancel_sep.show(); + } else if (state === 'display'){ + this._state = state; + this._edit_btn.show(); + this._cancel_btn.hide(); + this._cancel_sep.hide(); + this._save_btn.hide(); + } +}; + +TagWikiEditor.prototype.restoreContent = function(){ + var content_box = this._content_box; + content_box.empty(); + $.each(this._content_backup, function(idx, element){ + content_box.append(element); + }); +}; + +TagWikiEditor.prototype.getTagId = function(){ + return this._tag_id; +}; + +TagWikiEditor.prototype.isEditorLoaded = function(){ + return this._is_editor_loaded; +}; + +TagWikiEditor.prototype.setEditorLoaded = function(){ + return this._is_editor_loaded = true; +}; + +/** + * loads initial data for the editor input and activates + * the editor + */ +TagWikiEditor.prototype.startActivatingEditor = function(){ + var editor = this._editor; + var me = this; + $.ajax({ + type: 'GET', + url: askbot['urls']['load_tag_wiki_text'], + data: {tag_id: me.getTagId()}, + cache: false, + success: function(data){ + me.backupContent(); + editor.setMarkdown(data); + me.setContent(editor.getElement()); + me.setState('edit'); + if (me.isEditorLoaded() === false){ + editor.start(); + me.setEditorLoaded(); + } + } + }); +}; + +TagWikiEditor.prototype.saveData = function(){ + var me = this; + var text = this._editor.getMarkdown(); + $.ajax({ + type: 'POST', + dataType: 'json', + url: askbot['urls']['save_tag_wiki_text'], + data: {tag_id: me.getTagId(), text: text}, + cache: false, + success: function(data){ + if (data['success']){ + me.setState('display'); + me.setContent(data['html']); + } else { + showMessage(me.getElement(), data['message']); + } + } + }); +}; + +TagWikiEditor.prototype.cancelEdit = function(){ + this.restoreContent(); + this.setState('display'); +}; + +TagWikiEditor.prototype.decorate = function(element){ + //expect <div id='group-wiki-{{id}}'><div class="content"/><a class="edit"/></div> + this._element = element; + var edit_btn = element.find('.edit-description'); + this._edit_btn = edit_btn; + + //adding two buttons... + var save_btn = this.makeElement('a'); + save_btn.html(gettext('save')); + edit_btn.after(save_btn); + save_btn.hide(); + this._save_btn = save_btn; + + var cancel_btn = this.makeElement('a'); + cancel_btn.html(gettext('cancel')); + save_btn.after(cancel_btn); + cancel_btn.hide(); + this._cancel_btn = cancel_btn; + + this._cancel_sep = $('<span> | </span>'); + cancel_btn.before(this._cancel_sep); + this._cancel_sep.hide(); + + this._content_box = element.find('.content'); + this._tag_id = element.attr('id').split('-').pop(); + + var me = this; + var editor = new WMD(); + if (this._enabled_editor_buttons){ + editor.setEnabledButtons(this._enabled_editor_buttons); + } + editor.setPreviewerEnabled(this._is_previewer_enabled); + editor.setEscapeHandler(function(){me.cancelEdit()}); + this._editor = editor; + setupButtonEventHandlers(edit_btn, function(){ me.startActivatingEditor() }); + setupButtonEventHandlers(cancel_btn, function(){me.cancelEdit()}); + setupButtonEventHandlers(save_btn, function(){me.saveData()}); +}; + +var ImageChanger = function(){ + WrappedElement.call(this); + this._image_element = undefined; + this._delete_button = undefined; + this._save_url = undefined; + this._delete_url = undefined; + this._messages = undefined; +}; +inherits(ImageChanger, WrappedElement); + +ImageChanger.prototype.setImageElement = function(image_element){ + this._image_element = image_element; +}; + +ImageChanger.prototype.setMessages = function(messages){ + this._messages = messages; +}; + +ImageChanger.prototype.setDeleteButton = function(delete_button){ + this._delete_button = delete_button; +}; + +ImageChanger.prototype.setSaveUrl = function(url){ + this._save_url = url; +}; + +ImageChanger.prototype.setDeleteUrl = function(url){ + this._delete_url = url; +}; + +ImageChanger.prototype.setAjaxData = function(data){ + this._ajax_data = data; +}; + +ImageChanger.prototype.showImage = function(image_url){ + this._image_element.attr('src', image_url); + this._image_element.show(); +}; + +ImageChanger.prototype.deleteImage = function(){ + this._image_element.attr('src', ''); + this._image_element.css('display', 'none'); + + var me = this; + var delete_url = this._delete_url; + var data = this._ajax_data; + $.ajax({ + type: 'POST', + dataType: 'json', + url: delete_url, + data: data, + cache: false, + success: function(data){ + if (data['success'] === true){ + showMessage(me.getElement(), data['message'], 'after'); + } + } + }); +}; + +ImageChanger.prototype.saveImageUrl = function(image_url){ + var me = this; + var data = this._ajax_data; + data['image_url'] = image_url; + var save_url = this._save_url; + $.ajax({ + type: 'POST', + dataType: 'json', + url: save_url, + data: data, + cache: false, + success: function(data){ + if (!data['success']){ + showMessage(me.getElement(), data['message'], 'after'); + } + } + }); +}; + +ImageChanger.prototype.startDialog = function(){ + //reusing the wmd's file uploader + var me = this; + var change_image_text = this._messages['change_image']; + var change_image_button = this._element; + Attacklab.Util.prompt( + "<h3>" + gettext('Enter the logo url or upload an image') + '</h3>', + 'http://', + function(image_url){ + if (image_url){ + me.saveImageUrl(image_url); + me.showImage(image_url); + change_image_button.html(change_image_text); + me.showDeleteButton(); + } + }, + 'image' + ); +}; + +ImageChanger.prototype.showDeleteButton = function(){ + this._delete_button.show(); + this._delete_button.prev().show(); +}; + +ImageChanger.prototype.hideDeleteButton = function(){ + this._delete_button.hide(); + this._delete_button.prev().hide(); +}; + + +ImageChanger.prototype.startDeleting = function(){ + if (confirm(gettext('Do you really want to remove the image?'))){ + this.deleteImage(); + this._element.html(this._messages['add_image']); + this.hideDeleteButton(); + this._delete_button.hide(); + var sep = this._delete_button.prev(); + sep.hide(); + }; +}; + +/** + * decorates an element that will serve as the image changer button + */ +ImageChanger.prototype.decorate = function(element){ + this._element = element; + var me = this; + setupButtonEventHandlers( + element, + function(){ + me.startDialog(); + } + ); + setupButtonEventHandlers( + this._delete_button, + function(){ + me.startDeleting(); + } + ) +}; + +var UserGroupProfileEditor = function(){ + TagWikiEditor.call(this); +}; +inherits(UserGroupProfileEditor, TagWikiEditor); + +UserGroupProfileEditor.prototype.toggleEmailModeration = function(){ + var btn = this._moderate_email_btn; + var group_id = this.getTagId(); + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + data: {group_id: group_id}, + url: askbot['urls']['toggle_group_email_moderation'], + success: function(data){ + if (data['success']){ + btn.html(data['new_button_text']); + } else { + showMessage(btn, data['message']); + } + } + }); +}; + +UserGroupProfileEditor.prototype.decorate = function(element){ + this.setEnabledEditorButtons('bold italic link ol ul'); + this.setPreviewerEnabled(false); + UserGroupProfileEditor.superClass_.decorate.call(this, element); + var change_logo_btn = element.find('.change-logo'); + this._change_logo_btn = change_logo_btn; + + var moderate_email_toggle = new TwoStateToggle(); + moderate_email_toggle.setPostData({ + group_id: this.getTagId(), + property_name: 'moderate_email' + }); + var moderate_email_btn = element.find('#moderate-email'); + this._moderate_email_btn = moderate_email_btn; + moderate_email_toggle.decorate(moderate_email_btn); + + var open_group_toggle = new TwoStateToggle(); + open_group_toggle.setPostData({ + group_id: this.getTagId(), + property_name: 'is_open' + }); + var open_group_btn = element.find('#open-or-close-group'); + open_group_toggle.decorate(open_group_btn); + + var email_editor = new TextPropertyEditor(); + email_editor.decorate(element.find('#preapproved-emails')); + + var domain_editor = new TextPropertyEditor(); + domain_editor.decorate(element.find('#preapproved-email-domains')); + + var logo_changer = new ImageChanger(); + logo_changer.setImageElement(element.find('.group-logo')); + logo_changer.setAjaxData({ + group_id: this.getTagId() + }); + logo_changer.setSaveUrl(askbot['urls']['save_group_logo_url']); + logo_changer.setDeleteUrl(askbot['urls']['delete_group_logo_url']); + logo_changer.setMessages({ + change_image: gettext('change logo'), + add_image: gettext('add logo') + }); + var delete_logo_btn = element.find('.delete-logo'); + logo_changer.setDeleteButton(delete_logo_btn); + logo_changer.decorate(change_logo_btn); +}; + +var GroupJoinButton = function(group_id){ + TwoStateToggle.call(this); + this._group_id = group_id; +}; +inherits(GroupJoinButton, TwoStateToggle); + +GroupJoinButton.prototype.getPostData = function(){ + return { group_id: this._group_id }; +}; + +GroupJoinButton.prototype.getHandler = function(){ + var me = this; + return function(){ + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + data: me.getPostData(), + url: askbot['urls']['join_or_leave_group'], + success: function(data){ + if (data['success']){ + me.setOn(data['is_member']); + } else { + showMessage(me.getElement(), data['message']); + } + } + }); + }; +}; + $(document).ready(function() { $('[id^="comments-for-"]').each(function(index, element){ var comments = new PostCommentsWidget(); diff --git a/askbot/skins/common/media/js/tag_selector.js b/askbot/skins/common/media/js/tag_selector.js index 445a1e44..d5482992 100644 --- a/askbot/skins/common/media/js/tag_selector.js +++ b/askbot/skins/common/media/js/tag_selector.js @@ -1,4 +1,3 @@ - var TagDetailBox = function(box_type){ WrappedElement.call(this); this.box_type = box_type; @@ -101,17 +100,20 @@ function pickedTags(){ var interestingTags = {}; var ignoredTags = {}; + var subscribedTags = {}; var interestingTagDetailBox = new TagDetailBox('interesting'); var ignoredTagDetailBox = new TagDetailBox('ignored'); + var subscribedTagDetailBox = new TagDetailBox('subscribed'); var sendAjax = function(tagnames, reason, action, callback){ var url = ''; - if (action == 'add'){ - if (reason == 'good'){ + if (action == 'add') { + if (reason == 'good') { url = askbot['urls']['mark_interesting_tag']; - } - else { + } else if (reason == 'bad') { url = askbot['urls']['mark_ignored_tag']; + } else { + url = askbot['urls']['mark_subscribed_tag']; } } else { @@ -154,19 +156,23 @@ function pickedTags(){ var getTagList = function(reason){ var base_selector = '.marked-tags'; - if (reason === 'good'){ + if (reason === 'good') { var extra_selector = '.interesting'; - } else { + } else if (reason === 'bad') { var extra_selector = '.ignored'; + } else if (reason === 'subscribed') { + var extra_selector = '.subscribed'; } return $(base_selector + extra_selector); }; var getWildcardTagDetailBox = function(reason){ - if (reason === 'good'){ + if (reason === 'good') { return interestingTagDetailBox; - } else { + } else if (reason === 'bad') { return ignoredTagDetailBox; + } else if (reason === 'subscribed') { + return subscribedTagDetailBox; } }; @@ -230,27 +236,31 @@ function pickedTags(){ var to_target = interestingTags; var from_target = ignoredTags; var to_tag_container; - if (reason == 'bad'){ + if (reason === 'bad') { var input_sel = '#ignoredTagInput'; to_target = ignoredTags; from_target = interestingTags; to_tag_container = $('div .tags.ignored'); - } - else if (reason == 'good'){ + } else if (reason === 'good') { var input_sel = '#interestingTagInput'; to_tag_container = $('div .tags.interesting'); - } - else { + } else if (reason === 'subscribed') { + var input_sel = '#subscribedTagInput'; + to_target = subscribedTags; + to_tag_container = $('div .tags.subscribed'); + } else { return; } var tagnames = getUniqueWords($(input_sel).attr('value')); - $.each(tagnames, function(idx, tagname){ - if (tagname in from_target){ - unpickTag(from_target,tagname,reason,false); - } - }); + if (reason !== 'subscribed') {//for "subscribed" we do not remove + $.each(tagnames, function(idx, tagname) { + if (tagname in from_target) { + unpickTag(from_target, tagname, reason, false); + } + }); + } var clean_tagnames = []; $.each(tagnames, function(idx, tagname){ @@ -281,15 +291,16 @@ function pickedTags(){ }; var collectPickedTags = function(section){ - if (section === 'interesting'){ + if (section === 'interesting') { var reason = 'good'; var tag_store = interestingTags; - } - else if (section === 'ignored'){ + } else if (section === 'ignored') { var reason = 'bad'; var tag_store = ignoredTags; - } - else { + } else if (section === 'subscribed') { + var reason = 'subscribed'; + var tag_store = subscribedTags; + } else { return; } $('.' + section + '.tags.marked-tags .tag-left').each( @@ -344,7 +355,9 @@ function pickedTags(){ init: function(){ collectPickedTags('interesting'); collectPickedTags('ignored'); + collectPickedTags('subscribed'); setupTagFilterControl('display'); + setupTagFilterControl('email'); var ac = new AutoCompleter({ url: askbot['urls']['get_tag_list'], preloadData: true, @@ -364,8 +377,13 @@ function pickedTags(){ ignoredTagAc.decorate($('#ignoredTagInput')); ignoredTagAc.setOption('onItemSelect', getResultCallback('bad')); + var subscribedTagAc = $.extend(true, {}, ac); + subscribedTagAc.decorate($('#subscribedTagInput')); + subscribedTagAc.setOption('onItemSelect', getResultCallback('subscribed')); + $("#interestingTagAdd").click(getResultCallback('good')); $("#ignoredTagAdd").click(getResultCallback('bad')); + $("#subscribedTagAdd").click(getResultCallback('subscribed')); } }; } diff --git a/askbot/skins/common/media/js/user.js b/askbot/skins/common/media/js/user.js index 5d205560..b7dc0951 100644 --- a/askbot/skins/common/media/js/user.js +++ b/askbot/skins/common/media/js/user.js @@ -1,4 +1,4 @@ -$(document).ready(function(){ +var setup_inbox = function(){ var getSelected = function(){ @@ -18,7 +18,7 @@ $(document).ready(function(){ }; var submit = function(id_list, elements, action_type){ - if (action_type == 'delete' || action_type == 'mark_new' || action_type == 'mark_seen' || action_type == 'remove_flag' || action_type == 'close' || action_type == 'delete_post'){ + if (action_type == 'delete' || action_type == 'mark_new' || action_type == 'mark_seen' || action_type == 'remove_flag' || action_type == 'delete_post'){ $.ajax({ type: 'POST', cache: false, @@ -26,8 +26,8 @@ $(document).ready(function(){ data: JSON.stringify({memo_list: id_list, action_type: action_type}), url: askbot['urls']['manageInbox'], success: function(response_data){ - if (response_data['success'] === true){ - if (action_type == 'delete' || action_type == 'remove_flag' || action_type == 'close' || action_type == 'delete_post'){ + if (response_data['success'] == true){ + if (action_type == 'delete' || action_type == 'remove_flag' || action_type == 'delete_post'){ elements.remove(); } else if (action_type == 'mark_new'){ @@ -69,15 +69,11 @@ $(document).ready(function(){ } } if (action_type == 'remove_flag'){ - msg = ngettext('Remove all flags on this entry?', - 'Remove all flags on these entries?', data['id_list'].length); - if (confirm(msg) === false){ - return; - } - } - if (action_type == 'delete_post'){ - msg = ngettext('Delete this entry?', - 'Delete these entries?', data['id_list'].length); + msg = ngettext( + 'Remove all flags and approve this entry?', + 'Remove all flags and approve these entries?', + data['id_list'].length + ); if (confirm(msg) === false){ return; } @@ -88,8 +84,7 @@ $(document).ready(function(){ setupButtonEventHandlers($('#re_mark_new'), function(){startAction('mark_new')}); setupButtonEventHandlers($('#re_dismiss'), function(){startAction('delete')}); setupButtonEventHandlers($('#re_remove_flag'), function(){startAction('remove_flag')}); - setupButtonEventHandlers($('#re_close'), function(){startAction('close')}); - setupButtonEventHandlers($('#re_delete_post'), function(){startAction('delete_post')}); + //setupButtonEventHandlers($('#re_close'), function(){startAction('close')}); setupButtonEventHandlers( $('#sel_all'), function(){ @@ -117,16 +112,410 @@ $(document).ready(function(){ } ); - setupButtonEventHandlers($('.re_expand'), - function(e){ - e.preventDefault(); - var re_snippet = $(this).find(".re_snippet:first") - var re_content = $(this).find(".re_content:first") - $(re_snippet).slideToggle(); - $(re_content).slideToggle(); - } + var reject_post_dialog = new RejectPostDialog(); + reject_post_dialog.decorate($('#reject-edit-modal')); + setupButtonEventHandlers( + $('#re_delete_post'), + function(){ + var data = getSelected(); + if (data['id_list'].length === 0){ + return; + } + reject_post_dialog.setSelectedEditData(data); + reject_post_dialog.show(); + } ); -}); + //setupButtonEventHandlers($('.re_expand'), + // function(e){ + // e.preventDefault(); + // var re_snippet = $(this).find(".re_snippet:first") + // var re_content = $(this).find(".re_content:first") + // $(re_snippet).slideToggle(); + // $(re_content).slideToggle(); + // } + //); +}; + +var setup_badge_details_toggle = function(){ + $('.badge-context-toggle').each(function(idx, elem){ + var context_list = $(elem).parent().next('ul'); + if (context_list.children().length > 0){ + $(elem).addClass('active'); + var toggle_display = function(){ + if (context_list.css('display') == 'none'){ + $('.badge-context-list').hide(); + context_list.show(); + } else { + context_list.hide(); + } + }; + $(elem).click(toggle_display); + } + }); +}; + +/** + * @constructor + * manages post/edit reject reasons + * in the post moderation view + */ +var RejectPostDialog = function(){ + WrappedElement.call(this); + this._selected_edit_ids = null; + this._selected_reason_id = null; + this._state = null;//'select', 'preview', 'add-new' +}; +inherits(RejectPostDialog, WrappedElement); + +RejectPostDialog.prototype.setSelectedEditData = function(data){ + this._selected_edit_data = data; +}; + +RejectPostDialog.prototype.setState = function(state){ + this._state = state; + this.clearErrors(); + if (this._element){ + this._selector.hide(); + this._adder.hide(); + this._previewer.hide(); + if (state === 'select'){ + this._selector.show(); + } else if (state === 'preview'){ + this._previewer.show(); + } else if (state === 'add-new'){ + this._adder.show(); + } + } +}; + +RejectPostDialog.prototype.show = function(){ + $(this._element).modal('show'); +}; + +RejectPostDialog.prototype.hide = function(){ + $(this._element).modal('hide'); +}; + +RejectPostDialog.prototype.resetInputs = function(){ + if (this._title_input){ + this._title_input.reset(); + } + if (this._details_input){ + this._details_input.reset(); + } + var selected = this._element.find('.selected'); + selected.removeClass('selected'); +}; + +RejectPostDialog.prototype.clearErrors = function(){ + var error = this._element.find('.alert'); + error.remove(); +}; + +RejectPostDialog.prototype.makeAlertBox = function(errors){ + //construct the alert box + var alert_box = new AlertBox(); + alert_box.setClass('alert-error'); + if (typeof errors === "string"){ + alert_box.setText(errors); + } else if (errors.constructor === [].constructor){ + if (errors.length > 1){ + alert_box.setContent( + '<div>' + + gettext('Looks there are some things to fix:') + + '</div>' + ) + var list = this.makeElement('ul'); + $.each(errors, function(idx, item){ + list.append('<li>' + item + '</li>'); + }); + alert_box.addContent(list); + } else if (errors.length == 1){ + alert_box.setContent(errors[0]); + } else if (errors.length == 0){ + return; + } + } else if ('html' in errors){ + alert_box.setContent(errors); + } else { + return;//don't know what to do + } + return alert_box; +}; + +RejectPostDialog.prototype.setAdderErrors = function(errors){ + //clear previous errors + this.clearErrors(); + var alert_box = this.makeAlertBox(errors); + this._element + .find('#reject-edit-modal-add-new .modal-body') + .prepend(alert_box.getElement()); +}; + +RejectPostDialog.prototype.setSelectorErrors = function(errors){ + this.clearErrors(); + var alert_box = this.makeAlertBox(errors); + this._element + .find('#reject-edit-modal-select .modal-body') + .prepend(alert_box.getElement()); +}; + +RejectPostDialog.prototype.setErrors = function(errors){ + this.clearErrors(); + var alert_box = this.makeAlertBox(errors); + var current_state = this._state; + this._element + .find('#reject-edit-modal-' + current_state + ' .modal-body') + .prepend(alert_box.getElement()); +}; + +RejectPostDialog.prototype.addSelectableReason = function(data){ + var id = data['reason_id']; + var title = data['title']; + var details = data['details']; + this._select_box.addItem(id, title, details); +}; + +RejectPostDialog.prototype.startSavingReason = function(callback){ + + var title_input = this._title_input; + var details_input = this._details_input; + + var errors = []; + if (title_input.isBlank()){ + errors.push(gettext('Please provide description.')); + } + if (details_input.isBlank()){ + errors.push(gettext('Please provide details.')); + } + + if (errors.length > 0){ + this.setAdderErrors(errors); + return;//just show errors and quit + } + + var data = { + title: title_input.getVal(), + details: details_input.getVal() + }; + if (this._selected_reason_id){ + data['reason_id'] = this._selected_reason_id; + } + + var me = this; + + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + url: askbot['urls']['save_post_reject_reason'], + data: data, + success: function(data){ + if (data['success']){ + //show current reason data and focus on it + if (callback){ + callback(data); + } else { + me.addSelectableReason(data); + me.setState('select'); + } + } else { + me.setAdderErrors(data['message']); + } + } + }); +}; + +RejectPostDialog.prototype.rejectPost = function(reason_id){ + var me = this; + var memos = this._selected_edit_data['elements']; + var memo_ids = this._selected_edit_data['id_list']; + var data = { + reject_reason_id: reason_id, + memo_list: memo_ids, + action_type: 'delete_post' + } + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + data: JSON.stringify(data), + url: askbot['urls']['manageInbox'], + success: function(data){ + if (data['success']){ + memos.remove(); + me.hide(); + } else { + //only fatal errors here + me.setErrors(data['message']); + } + } + }); +}; + +RejectPostDialog.prototype.setPreviewerData = function(data){ + this._selected_reason_id = data['id']; + this._element.find('.selected-reason-title').html(data['title']); + this._element.find('.selected-reason-details').html(data['details']); +}; + +RejectPostDialog.prototype.startEditingReason = function(){ + var title = this._element.find('.selected-reason-title').html(); + var details = this._element.find('.selected-reason-details').html(); + this._title_input.setVal(title); + this._details_input.setVal(details); + this.setState('add-new'); +}; + +RejectPostDialog.prototype.resetSelectedReasonId = function(){ + this._selected_reason_id = null; +}; + +RejectPostDialog.prototype.getSelectedReasonId = function(){ + return this._selected_reason_id; +}; + +RejectPostDialog.prototype.startDeletingReason = function(){ + var select_box = this._select_box; + var data = select_box.getSelectedItemData(); + var reason_id = data['id']; + var me = this; + if (data['id']){ + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + url: askbot['urls']['delete_post_reject_reason'], + data: {reason_id: reason_id}, + success: function(data){ + if (data['success']){ + select_box.removeItem(reason_id); + } else { + me.setSelectorErrors(data['message']); + } + } + }); + } else { + me.setSelectorErrors( + gettext('A reason must be selected to delete one.') + ) + } +}; + +RejectPostDialog.prototype.decorate = function(element){ + this._element = element; + //set default state according to the # of available reasons + this._selector = $(element).find('#reject-edit-modal-select'); + this._adder = $(element).find('#reject-edit-modal-add-new'); + this._previewer = $(element).find('#reject-edit-modal-preview'); + if (this._selector.find('li').length > 0){ + this.setState('select'); + this.resetInputs(); + } else { + this.setState('add-new'); + this.resetInputs(); + } + + $(this._element).find('.dropdown-toggle').dropdown(); + + var select_box = new SelectBox(); + select_box.decorate($(this._selector.find('.select-box'))); + this._select_box = select_box; + + //setup tipped-inputs + var reject_title_input = $(this._element).find('input'); + var title_input = new TippedInput(); + title_input.decorate($(reject_title_input)); + this._title_input = title_input; + + var reject_details_input = $(this._element) + .find('textarea.reject-reason-details'); + + var details_input = new TippedInput(); + details_input.decorate($(reject_details_input)); + this._details_input = details_input; + + var me = this; + setupButtonEventHandlers( + element.find('.cancel, .modal-header .close'), + function() { + me.hide(); + me.clearErrors(); + me.resetInputs(); + me.resetSelectedReasonId(); + me.setState('select'); + } + ); + + setupButtonEventHandlers( + $(this._element).find('.save-reason'), + function(){ me.startSavingReason() } + ); + + setupButtonEventHandlers( + $(this._element).find('.save-reason-and-reject'), + function(){ + me.startSavingReason( + function(data){ + me.rejectPost(data['reason_id']); + } + ); + } + ); + + setupButtonEventHandlers( + $(this._element).find('.reject'), + function(){ + me.rejectPost(me.getSelectedReasonId()); + } + ); + + setupButtonEventHandlers( + element.find('.select-other-reason'), + function(){ + me.resetInputs(); + me.setState('select'); + } + ) + + setupButtonEventHandlers( + element.find('.add-new-reason'), + function(){ + me.resetSelectedReasonId(); + me.resetInputs(); + me.setState('add-new') + } + ); + + setupButtonEventHandlers( + element.find('.select-this-reason'), + function(){ + var data = select_box.getSelectedItemData(); + if (data['id']){ + me.setState('preview'); + me.setPreviewerData(data); + } else { + me.setSelectorErrors( + gettext('A reason must be selected to reject post.') + ) + } + } + ); + + setupButtonEventHandlers( + element.find('.edit-reason'), + function(){ + me.startEditingReason(); + } + ); + + setupButtonEventHandlers( + element.find('.delete-this-reason'), + function(){ + me.startDeletingReason(); + } + ) +}; /** * @constructor @@ -204,6 +593,247 @@ FollowUser.prototype.toggleState = function(){ } }; +/** + * @constructor + * @param {string} name + */ +var UserGroup = function(name){ + WrappedElement.call(this); + this._name = name; + this._content = null; +}; +inherits(UserGroup, WrappedElement); + +UserGroup.prototype.getDeleteHandler = function(){ + var group_name = this._name; + var me = this; + var groups_container = me._groups_container; + return function(){ + var data = { + user_id: askbot['data']['viewUserId'], + group_name: group_name, + action: 'remove' + }; + $.ajax({ + type: 'POST', + dataType: 'json', + data: data, + cache: false, + url: askbot['urls']['edit_group_membership'], + success: function(){ + groups_container.removeGroup(me); + } + }); + }; +}; + +UserGroup.prototype.setContent = function(content){ + this._content = content; +}; + +UserGroup.prototype.getName = function(){ + return this._name; +}; + +UserGroup.prototype.setGroupsContainer = function(container){ + this._groups_container = container; +}; + +UserGroup.prototype.decorate = function(element){ + this._element = element; + this._name = $.trim(element.find('a').html()); + var deleter = new DeleteIcon(); + deleter.setHandler(this.getDeleteHandler()); + deleter.setContent('x'); + this._element.find('.group-name').append(deleter.getElement()); + this._delete_icon = deleter; +}; + +UserGroup.prototype.createDom = function(){ + var element = this.makeElement('li'); + element.html(this._content); + this._element = element; + this.decorate(element); +}; + +UserGroup.prototype.dispose = function(){ + this._delete_icon.dispose(); + this._element.remove(); +}; + +/** + * @constructor + */ +var GroupsContainer = function(){ + WrappedElement.call(this); +}; +inherits(GroupsContainer, WrappedElement); + +GroupsContainer.prototype.decorate = function(element){ + this._element = element; + var groups = []; + var group_names = []; + var me = this; + //collect list of groups + $.each(element.find('li'), function(idx, li){ + var group = new UserGroup(); + group.setGroupsContainer(me); + group.decorate($(li)); + groups.push(group); + group_names.push(group.getName()); + }); + this._groups = groups; + this._group_names = group_names; +}; + +GroupsContainer.prototype.addGroup = function(group_data){ + var group_name = group_data['name']; + if ($.inArray(group_name, this._group_names) > -1){ + return; + } + var group = new UserGroup(group_name); + group.setContent(group_data['html']); + group.setGroupsContainer(this); + this._groups.push(group); + this._group_names.push(group_name); + this._element.append(group.getElement()); +}; + +GroupsContainer.prototype.removeGroup = function(group){ + var idx = $.inArray(group, this._groups); + if (idx === -1){ + return; + } + this._groups.splice(idx, 1); + this._group_names.splice(idx, 1); + group.dispose(); +}; + +var GroupAdderWidget = function(){ + WrappedElement.call(this); + this._state = 'display';//display or edit +}; +inherits(GroupAdderWidget, WrappedElement); + +/** + * @param {string} state + */ +GroupAdderWidget.prototype.setState = function(state){ + if (state === 'display'){ + this._element.html(gettext('add group')); + this._input.hide(); + this._input.val(''); + this._button.hide(); + } else if (state === 'edit'){ + this._element.html(gettext('cancel')); + this._input.show(); + this._input.focus(); + this._button.show(); + } else { + return; + } + this._state = state; +}; + +GroupAdderWidget.prototype.getValue = function(){ + return this._input.val(); +}; + +GroupAdderWidget.prototype.addGroup = function(group_data){ + this._groups_container.addGroup(group_data); +}; + +GroupAdderWidget.prototype.getAddGroupHandler = function(){ + var me = this; + return function(){ + var group_name = me.getValue(); + var data = { + group_name: group_name, + user_id: askbot['data']['viewUserId'], + action: 'add' + }; + $.ajax({ + type: 'POST', + dataType: 'json', + data: data, + cache: false, + url: askbot['urls']['edit_group_membership'], + success: function(data){ + if (data['success'] == true){ + me.addGroup(data); + me.setState('display'); + } else { + var message = data['message']; + showMessage(me.getElement(), message, 'after'); + } + } + }); + }; +}; + +GroupAdderWidget.prototype.setGroupsContainer = function(container){ + this._groups_container = container; +}; + +GroupAdderWidget.prototype.toggleState = function(){ + if (this._state === 'display'){ + this.setState('edit'); + } else if (this._state === 'edit'){ + this.setState('display'); + } +}; + +GroupAdderWidget.prototype.decorate = function(element){ + this._element = element; + var input = this.makeElement('input'); + this._input = input; + + var groupsAc = new AutoCompleter({ + url: askbot['urls']['get_groups_list'], + preloadData: true, + minChars: 1, + useCache: true, + matchInside: false, + maxCacheLength: 100, + delay: 10 + }); + groupsAc.decorate(input); + + var button = this.makeElement('button'); + button.html(gettext('add')); + this._button = button; + element.before(input); + input.after(button); + this.setState('display'); + setupButtonEventHandlers(button, this.getAddGroupHandler()); + var me = this; + setupButtonEventHandlers( + element, + function(){ me.toggleState() } + ); +}; + +/** + * @constructor + * allows editing user groups + */ +var UserGroupsEditor = function(){ + WrappedElement.call(this); +}; +inherits(UserGroupsEditor, WrappedElement); + +UserGroupsEditor.prototype.decorate = function(element){ + this._element = element; + var add_link = element.find('#add-group'); + var adder = new GroupAdderWidget(); + adder.decorate(add_link); + + var groups_container = new GroupsContainer(); + groups_container.decorate(element.find('ul')); + adder.setGroupsContainer(groups_container); + //todo - add group deleters +}; + (function(){ var fbtn = $('.follow-toggle'); if (fbtn.length === 1){ @@ -211,5 +841,10 @@ FollowUser.prototype.toggleState = function(){ follow_user.decorate(fbtn); follow_user.setUserName(askbot['data']['viewUserName']); } + if (askbot['data']['userIsAdminOrMod']){ + var group_editor = new UserGroupsEditor(); + group_editor.decorate($('#user-groups')); + } else { + $('#add-group').remove(); + } })(); - diff --git a/askbot/skins/common/media/js/utils.js b/askbot/skins/common/media/js/utils.js index 9e02b5d4..297e3f9a 100644 --- a/askbot/skins/common/media/js/utils.js +++ b/askbot/skins/common/media/js/utils.js @@ -216,7 +216,6 @@ QSutils.add_search_tag = function(query_string, tag){ return this.patch_query_string(query_string, 'tags:' + tag_string); }; - /* **************************************************** */ /* some google closure-like code for the ui elements */ @@ -240,6 +239,9 @@ WrappedElement.prototype.setElement = function(element){ WrappedElement.prototype.createDom = function(){ this._element = $('<div></div>'); }; +WrappedElement.prototype.decorate = function(element){ + this._element = element; +}; WrappedElement.prototype.getElement = function(){ if (this._element === null){ this.createDom(); @@ -264,6 +266,152 @@ WrappedElement.prototype.dispose = function(){ this._in_document = false; }; +/** + * Can be used for an input box or textarea. + * The original value will be treated as an instruction. + * When user focuses on the field, the tip will be gone, + * when the user escapes without typing anything besides + * perhaps empty text, the instruction is restored. + * When instruction is shown, class "blank" is present + * in the input/textare element. + */ +var TippedInput = function(){ + WrappedElement.call(this); + this._instruction = null; +}; +inherits(TippedInput, WrappedElement); + +TippedInput.prototype.reset = function(){ + $(this._element).val(this._instruction); + $(this._element).addClass('blank'); +}; + +TippedInput.prototype.isBlank = function(){ + return this.getVal() === this._instruction; +}; + +TippedInput.prototype.getVal = function(){ + return this._element.val(); +}; + +TippedInput.prototype.setVal = function(value){ + if (value) { + this._element.val(value); + if (this.isBlank()){ + this._element.addClass('blank'); + } else { + this._element.removeClass('blank'); + } + } +}; + +TippedInput.prototype.decorate = function(element){ + this._element = element; + var instruction_text = this.getVal(); + this._instruction = instruction_text; + var me = this; + $(element).focus(function(){ + if (me.isBlank()){ + $(element) + .val('') + .removeClass('blank'); + } + }); + $(element).blur(function(){ + var val = $(element).val(); + if ($.trim(val) === ''){ + $(element) + .val(instruction_text) + .addClass('blank'); + } + }); + makeKeyHandler(13, function(){ + $(element).blur(); + }); +}; + +/** + * will setup a bootstrap.js alert + * programmatically + */ +var AlertBox = function(){ + WrappedElement.call(this); + this._text = null; +}; +inherits(AlertBox, WrappedElement); + +AlertBox.prototype.setClass = function(classes){ + this._classes = classes; + if (this._element){ + this._element.addClass(classes); + } +}; + +AlertBox.prototype.setError = function(state){ + this._is_error = state; + if (this._element) { + if (state === true) { + this._element.addClass('alert-error'); + } else { + this._element.removeClass('alert-error'); + } + } +}; + +AlertBox.prototype.setText = function(text){ + this._text = text; + if (this._content){ + this._content.html(text); + } +}; + +AlertBox.prototype.getContent = function(){ + if (this._content){ + return this._content; + } else { + this._content = this.makeElement('div'); + return this._content; + } +}; + +AlertBox.prototype.setContent = function(content){ + var container = this.getContent(); + container.empty() + container.append(content); +}; + +AlertBox.prototype.addContent = function(content){ + var container = this.getContent(); + container.append(content); +}; + +AlertBox.prototype.createDom = function(){ + this._element = this.makeElement('div'); + this._element.addClass('alert fade in'); + + if (this._is_error) { + this.setError(this._is_error); + } + + if (this._classes){ + this._element.addClass(this._classes); + } + + this._cancel_button = this.makeElement('button'); + this._cancel_button + .addClass('close') + .attr('data-dismiss', 'alert') + .html('×'); + this._element.append(this._cancel_button); + + this._element.append(this.getContent()); + if (this._text){ + this.setText(this._text); + } + + this._element.alert();//bootstrap.js alert +}; + var SimpleControl = function(){ WrappedElement.call(this); this._handler = null; @@ -278,6 +426,10 @@ SimpleControl.prototype.setHandler = function(handler){ } }; +SimpleControl.prototype.getHandler = function(){ + return this._handler; +}; + SimpleControl.prototype.setHandlerInternal = function(){ //default internal setHandler behavior setupButtonEventHandlers(this._element, this._handler); @@ -308,6 +460,7 @@ EditLink.prototype.decorate = function(element){ var DeleteIcon = function(title){ SimpleControl.call(this); this._title = title; + this._content = null; }; inherits(DeleteIcon, SimpleControl); @@ -327,6 +480,427 @@ DeleteIcon.prototype.setHandlerInternal = function(){ DeleteIcon.prototype.createDom = function(){ this._element = this.makeElement('span'); this.decorate(this._element); + if (this._content !== null){ + this.setContent(this._content); + } +}; + +DeleteIcon.prototype.setContent = function(content){ + if (this._element === null){ + this._content = content; + } else { + this._content = content; + this._element.html(content); + } +} + +/** + * attaches a modal menu with a text editor + * to a link. The modal menu is from bootstrap.js + */ +var TextPropertyEditor = function(){ + WrappedElement.call(this); + this._editor = null; +}; +inherits(TextPropertyEditor, WrappedElement); + +TextPropertyEditor.prototype.getWidgetData = function(){ + var data = this._element.data(); + return { + object_id: data['objectId'], + model_name: data['modelName'], + property_name: data['propertyName'], + url: data['url'], + help_text: data['helpText'], + editor_heading: data['editorHeading'] + }; +}; + +TextPropertyEditor.prototype.makeEditor = function(){ + if (this._editor) { + return this._editor; + } + var editor = this.makeElement('div') + .addClass('modal'); + this._editor = editor; + + var header = this.makeElement('div') + .addClass('modal-header'); + editor.append(header); + + var close_link = this.makeElement('div') + .addClass('close') + .attr('data-dismiss', 'modal') + .html('x'); + header.append(close_link); + + var title = this.makeElement('h3') + .html(this.getWidgetData()['editor_heading']); + header.append(title); + + var body = this.makeElement('div') + .addClass('modal-body'); + editor.append(body); + + var textarea = this.makeElement('textarea') + .addClass('tipped-input blank') + .val(this.getWidgetData()['help_text']); + body.append(textarea); + + var tipped_input = new TippedInput(); + tipped_input.decorate(textarea); + this._text_input = tipped_input; + + var footer = this.makeElement('div') + .addClass('modal-footer'); + editor.append(footer); + + var save_btn = this.makeElement('button') + .addClass('btn btn-primary') + .html(gettext('Save')); + footer.append(save_btn); + + var cancel_btn = this.makeElement('button') + .addClass('btn cancel') + .html(gettext('Cancel')); + footer.append(cancel_btn); + + var me = this; + setupButtonEventHandlers(save_btn, function(){ + me.saveData(); + }); + setupButtonEventHandlers(cancel_btn, function(){ + editor.modal('hide'); + }); + editor.modal('hide'); + + $(document).append(editor); + return editor; +}; + +TextPropertyEditor.prototype.openEditor = function(){ + this._editor.modal('show'); +}; + +TextPropertyEditor.prototype.clearMessages = function(){ + this._editor.find('.alert').remove(); +}; + +TextPropertyEditor.prototype.getAlert = function(){ + var box = new AlertBox(); + var modal_body = this._editor.find('.modal-body'); + modal_body.prepend(box.getElement()); + return box; +}; + +TextPropertyEditor.prototype.showAlert = function(text){ + this.clearMessages(); + var box = this.getAlert(); + box.setText(text); + return box; +}; + +TextPropertyEditor.prototype.showError = function(text){ + var box = this.showAlert(text); + box.setError(true); + return box; +}; + +TextPropertyEditor.prototype.setText = function(text){ + this._text_input.setVal(text); +}; + +TextPropertyEditor.prototype.getText = function(){ + return this._text_input.getVal(); +}; + +TextPropertyEditor.prototype.hideDialog = function(){ + this._editor.modal('hide'); +}; + +TextPropertyEditor.prototype.startOpeningEditor = function(){ + var me = this; + $.ajax({ + type: 'GET', + dataType: 'json', + cache: false, + url: me.getWidgetData()['url'], + data: me.getWidgetData(), + success: function(data){ + if (data['success']) { + me.makeEditor(); + me.setText($.trim(data['text'])); + me.openEditor(); + } else { + showMessage(me.getElement(), data['message']); + } + } + }); +}; + +TextPropertyEditor.prototype.saveData = function(){ + var data = this.getWidgetData(); + data['text'] = this.getText(); + var me = this; + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + url: me.getWidgetData()['url'], + data: data, + success: function(data) { + if (data['success']) { + me.showAlert(gettext('saved')); + setTimeout(function(){ + me.clearMessages(); + me.hideDialog(); + }, 1000); + } else { + me.showError(data['message']); + } + } + }); +}; + +TextPropertyEditor.prototype.decorate = function(element){ + this._element = element; + var me = this; + setupButtonEventHandlers(element, function(){ me.startOpeningEditor() }); +}; + +/** + * A button on which user can click + * and become added to some group (followers, group members, etc.) + * or toggle some state on/off + * The button has four states on-prompt, off-prompt, on-state and off-state + * on-prompt is activated on mouseover, when user is not part of group + * off-prompt - on mouseover, when user is part of group + * on-state - when user is part of group and mouse is not over the button + * off-state - same as above, but when user is not part of the group + */ +var TwoStateToggle = function(){ + SimpleControl.call(this); + this._state = null; + this._state_messages = {}; + this._states = [ + 'on-state', + 'off-state', + 'on-prompt', + 'off-prompt' + ]; + this._handler = this.getDefaultHandler(); + this._post_data = {}; + this.toggleUrl = '';//public property +}; +inherits(TwoStateToggle, SimpleControl); + +TwoStateToggle.prototype.setPostData = function(data){ + this._post_data = data; +}; + +TwoStateToggle.prototype.getPostData = function(){ + return this._post_data; +}; + +TwoStateToggle.prototype.resetStyles = function(){ + var element = this._element; + var states = this._states; + $.each(states, function(idx, state){ + element.removeClass(state); + }); + this._element.html(''); +}; + +TwoStateToggle.prototype.isOn = function(){ + return this._element.hasClass('on'); +}; + +TwoStateToggle.prototype.getDefaultHandler = function(){ + var me = this; + return function(){ + var data = me.getPostData(); + data['disable'] = me.isOn(); + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + url: me.toggleUrl, + data: data, + success: function(data) { + if (data['success']) { + if ( data['is_enabled'] ) { + me.setState('on-state'); + } else { + me.setState('off-state'); + } + } else { + showMessage(me.getElement(), data['message']); + } + } + }); + }; +}; + +TwoStateToggle.prototype.isCheckBox = function(){ + var element = this._element; + return element.attr('type') === 'checkbox'; +}; + +TwoStateToggle.prototype.setState = function(state){ + var element = this._element; + this._state = state; + if (element) { + this.resetStyles(); + element.addClass(state); + if (state === 'on-state') { + element.addClass('on'); + } else if (state === 'off-state') { + element.removeClass('on'); + } + if ( this.isCheckBox() ) { + if (state === 'on-state') { + element.attr('checked', true); + } else if (state === 'off-state') { + element.attr('checked', false); + } + } else { + this._element.html(this._state_messages[state]); + } + } +}; + +TwoStateToggle.prototype.decorate = function(element){ + this._element = element; + //read messages for all states + var messages = {}; + messages['on-state'] = + element.attr('data-on-state-text') || gettext('enabled'); + messages['off-state'] = + element.attr('data-off-state-text') || gettext('disabled'); + messages['on-prompt'] = + element.attr('data-on-prompt-text') || messages['on-state']; + messages['off-prompt'] = + element.attr('data-off-prompt-text') || messages['off-state']; + this._state_messages = messages; + + this.toggleUrl = element.attr('data-toggle-url'); + + //detect state and save it + if ( + element.attr('nodeName') === 'INPUT' && element.attr('type', 'checkbox') + ) { + this._state = element.attr('checked') ? 'state-on' : 'state-off'; + } else { + var text = $.trim(element.html()); + for (var i = 0; i < this._states.length; i++){ + var state = this._states[i]; + if (text === messages[state]){ + this._state = state; + break; + } + } + } + + //set mouseover handler + var me = this; + element.mouseover(function(){ + var is_on = me.isOn(); + if (is_on){ + me.setState('off-prompt'); + } else { + me.setState('on-prompt'); + } + element.css('background-color', 'red'); + return false; + }); + element.mouseout(function(){ + var is_on = me.isOn(); + if (is_on){ + me.setState('on-state'); + } else { + me.setState('off-state'); + } + element.css('background-color', 'white'); + return false; + }); + + setupButtonEventHandlers(element, this.getHandler()); +}; + +/** + * A list of items from where one can be selected + */ +var SelectBox = function(){ + WrappedElement.call(this); + this._items = []; +}; +inherits(SelectBox, WrappedElement); + +SelectBox.prototype.removeItem = function(id){ + var item = this.getItem(id); + item.fadeOut(); + item.remove(); +}; + +SelectBox.prototype.getItem = function(id){ + return $(this._element.find('li[data-item-id="' + id + '"]')); +}; + +SelectBox.prototype.addItem = function(id, title, details){ + /*this._items.push({ + id: id, + title: title, + details: details + });*/ + if (this._element){ + var li = this.getItem(id); + var new_li = false; + if (li.length !== 1){ + li = this.makeElement('li'); + new_li = true; + } + li.attr('data-item-id', id) + .attr('data-original-title', details) + .html(title); + if (new_li){ + this._element.append(li); + } + this.selectItem($(li)); + var me = this; + setupButtonEventHandlers( + $(li), + function(){ + me.selectItem($(li)); + } + ); + } +}; + +SelectBox.prototype.getSelectedItemData = function(){ + var item = $(this._element.find('li.selected')[0]); + return { + id: item.attr('data-item-id'), + title: item.html(), + details: item.attr('data-original-title') + }; +}; + +SelectBox.prototype.selectItem = function(item){ + this._element.find('li').removeClass('selected'); + item.addClass('selected'); +}; + +SelectBox.prototype.decorate = function(element){ + this._element = element; + var me = this; + this._element.find('li').each(function(itx, item){ + setupButtonEventHandlers( + $(item), + function(){ + me.selectItem($(item)); + } + ); + }); }; var Tag = function(){ @@ -490,3 +1064,205 @@ if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typ //our custom autocompleter var AutoCompleter=function(a){var b={autocompleteMultiple:true,multipleSeparator:" ",inputClass:"acInput",loadingClass:"acLoading",resultsClass:"acResults",selectClass:"acSelect",queryParamName:"q",limitParamName:"limit",extraParams:{},lineSeparator:"\n",cellSeparator:"|",minChars:2,maxItemsToShow:10,delay:400,useCache:true,maxCacheLength:10,matchSubset:true,matchCase:false,matchInside:true,mustMatch:false,preloadData:false,selectFirst:false,stopCharRegex:/\s+/,selectOnly:false,formatItem:null,onItemSelect:false,autoFill:false,filterResults:true,sortResults:true,sortFunction:false,onNoMatch:false};this.options=$.extend({},b,a);this.cacheData_={};this.cacheLength_=0;this.selectClass_="jquery-autocomplete-selected-item";this.keyTimeout_=null;this.lastKeyPressed_=null;this.lastProcessedValue_=null;this.lastSelectedValue_=null;this.active_=false;this.finishOnBlur_=true;this.options.minChars=parseInt(this.options.minChars,10);if(isNaN(this.options.minChars)||this.options.minChars<1){this.options.minChars=2}this.options.maxItemsToShow=parseInt(this.options.maxItemsToShow,10);if(isNaN(this.options.maxItemsToShow)||this.options.maxItemsToShow<1){this.options.maxItemsToShow=10}this.options.maxCacheLength=parseInt(this.options.maxCacheLength,10);if(isNaN(this.options.maxCacheLength)||this.options.maxCacheLength<1){this.options.maxCacheLength=10}if(this.options.preloadData===true){this.fetchRemoteData("",function(){})}};inherits(AutoCompleter,WrappedElement);AutoCompleter.prototype.decorate=function(a){this._element=a;this._element.attr("autocomplete","off");this._results=$("<div></div>").hide();if(this.options.resultsClass){this._results.addClass(this.options.resultsClass)}this._results.css({position:"absolute"});$("body").append(this._results);this.setEventHandlers()};AutoCompleter.prototype.setEventHandlers=function(){var a=this;a._element.keydown(function(b){a.lastKeyPressed_=b.keyCode;switch(a.lastKeyPressed_){case 38:b.preventDefault();if(a.active_){a.focusPrev()}else{a.activate()}return false;break;case 40:b.preventDefault();if(a.active_){a.focusNext()}else{a.activate()}return false;break;case 9:case 13:if(a.active_){b.preventDefault();a.selectCurrent();return false}break;case 27:if(a.active_){b.preventDefault();a.finish();return false}break;default:a.activate()}});a._element.blur(function(){if(a.finishOnBlur_){setTimeout(function(){a.finish()},200)}})};AutoCompleter.prototype.position=function(){var a=this._element.offset();this._results.css({top:a.top+this._element.outerHeight(),left:a.left})};AutoCompleter.prototype.cacheRead=function(d){var f,c,b,a,e;if(this.options.useCache){d=String(d);f=d.length;if(this.options.matchSubset){c=1}else{c=f}while(c<=f){if(this.options.matchInside){a=f-c}else{a=0}e=0;while(e<=a){b=d.substr(0,c);if(this.cacheData_[b]!==undefined){return this.cacheData_[b]}e++}c++}}return false};AutoCompleter.prototype.cacheWrite=function(a,b){if(this.options.useCache){if(this.cacheLength_>=this.options.maxCacheLength){this.cacheFlush()}a=String(a);if(this.cacheData_[a]!==undefined){this.cacheLength_++}return this.cacheData_[a]=b}return false};AutoCompleter.prototype.cacheFlush=function(){this.cacheData_={};this.cacheLength_=0};AutoCompleter.prototype.callHook=function(c,b){var a=this.options[c];if(a&&$.isFunction(a)){return a(b,this)}return false};AutoCompleter.prototype.activate=function(){var b=this;var a=function(){b.activateNow()};var c=parseInt(this.options.delay,10);if(isNaN(c)||c<=0){c=250}if(this.keyTimeout_){clearTimeout(this.keyTimeout_)}this.keyTimeout_=setTimeout(a,c)};AutoCompleter.prototype.activateNow=function(){var a=this.getValue();if(a!==this.lastProcessedValue_&&a!==this.lastSelectedValue_){if(a.length>=this.options.minChars){this.active_=true;this.lastProcessedValue_=a;this.fetchData(a)}}};AutoCompleter.prototype.fetchData=function(b){if(this.options.data){this.filterAndShowResults(this.options.data,b)}else{var a=this;this.fetchRemoteData(b,function(c){a.filterAndShowResults(c,b)})}};AutoCompleter.prototype.fetchRemoteData=function(c,e){var d=this.cacheRead(c);if(d){e(d)}else{var a=this;if(this._element){this._element.addClass(this.options.loadingClass)}var b=function(g){var f=false;if(g!==false){f=a.parseRemoteData(g);a.options.data=f;a.cacheWrite(c,f)}if(a._element){a._element.removeClass(a.options.loadingClass)}e(f)};$.ajax({url:this.makeUrl(c),success:b,error:function(){b(false)}})}};AutoCompleter.prototype.setOption=function(a,b){this.options[a]=b};AutoCompleter.prototype.setExtraParam=function(b,c){var a=$.trim(String(b));if(a){if(!this.options.extraParams){this.options.extraParams={}}if(this.options.extraParams[a]!==c){this.options.extraParams[a]=c;this.cacheFlush()}}};AutoCompleter.prototype.makeUrl=function(e){var a=this;var b=this.options.url;var d=$.extend({},this.options.extraParams);if(this.options.queryParamName===false){b+=encodeURIComponent(e)}else{d[this.options.queryParamName]=e}if(this.options.limitParamName&&this.options.maxItemsToShow){d[this.options.limitParamName]=this.options.maxItemsToShow}var c=[];$.each(d,function(f,g){c.push(a.makeUrlParam(f,g))});if(c.length){b+=b.indexOf("?")==-1?"?":"&";b+=c.join("&")}return b};AutoCompleter.prototype.makeUrlParam=function(a,b){return String(a)+"="+encodeURIComponent(b)};AutoCompleter.prototype.splitText=function(a){return String(a).replace(/(\r\n|\r|\n)/g,"\n").split(this.options.lineSeparator)};AutoCompleter.prototype.parseRemoteData=function(c){var h,b,f,d,g;var e=[];var b=this.splitText(c);for(f=0;f<b.length;f++){var a=b[f].split(this.options.cellSeparator);g=[];for(d=0;d<a.length;d++){g.push(unescape(a[d]))}h=g.shift();e.push({value:unescape(h),data:g})}return e};AutoCompleter.prototype.filterAndShowResults=function(a,b){this.showResults(this.filterResults(a,b),b)};AutoCompleter.prototype.filterResults=function(d,b){var f=[];var l,c,e,m,j,a;var k,h,g;for(e=0;e<d.length;e++){m=d[e];j=typeof m;if(j==="string"){l=m;c={}}else{if($.isArray(m)){l=m[0];c=m.slice(1)}else{if(j==="object"){l=m.value;c=m.data}}}l=String(l);if(l>""){if(typeof c!=="object"){c={}}if(this.options.filterResults){h=String(b);g=String(l);if(!this.options.matchCase){h=h.toLowerCase();g=g.toLowerCase()}a=g.indexOf(h);if(this.options.matchInside){a=a>-1}else{a=a===0}}else{a=true}if(a){f.push({value:l,data:c})}}}if(this.options.sortResults){f=this.sortResults(f,b)}if(this.options.maxItemsToShow>0&&this.options.maxItemsToShow<f.length){f.length=this.options.maxItemsToShow}return f};AutoCompleter.prototype.sortResults=function(c,d){var b=this;var a=this.options.sortFunction;if(!$.isFunction(a)){a=function(g,e,h){return b.sortValueAlpha(g,e,h)}}c.sort(function(f,e){return a(f,e,d)});return c};AutoCompleter.prototype.sortValueAlpha=function(d,c,e){d=String(d.value);c=String(c.value);if(!this.options.matchCase){d=d.toLowerCase();c=c.toLowerCase()}if(d>c){return 1}if(d<c){return -1}return 0};AutoCompleter.prototype.showResults=function(e,b){var k=this;var g=$("<ul></ul>");var f,l,j,a,h=false,d=false;var c=e.length;for(f=0;f<c;f++){l=e[f];j=$("<li>"+this.showResult(l.value,l.data)+"</li>");j.data("value",l.value);j.data("data",l.data);j.click(function(){var i=$(this);k.selectItem(i)}).mousedown(function(){k.finishOnBlur_=false}).mouseup(function(){k.finishOnBlur_=true});g.append(j);if(h===false){h=String(l.value);d=j;j.addClass(this.options.firstItemClass)}if(f==c-1){j.addClass(this.options.lastItemClass)}}this.position();this._results.html(g).show();a=this._results.outerWidth()-this._results.width();this._results.width(this._element.outerWidth()-a);$("li",this._results).hover(function(){k.focusItem(this)},function(){});if(this.autoFill(h,b)){this.focusItem(d)}};AutoCompleter.prototype.showResult=function(b,a){if($.isFunction(this.options.showResult)){return this.options.showResult(b,a)}else{return b}};AutoCompleter.prototype.autoFill=function(e,c){var b,a,d,f;if(this.options.autoFill&&this.lastKeyPressed_!=8){b=String(e).toLowerCase();a=String(c).toLowerCase();d=e.length;f=c.length;if(b.substr(0,f)===a){this._element.val(e);this.selectRange(f,d);return true}}return false};AutoCompleter.prototype.focusNext=function(){this.focusMove(+1)};AutoCompleter.prototype.focusPrev=function(){this.focusMove(-1)};AutoCompleter.prototype.focusMove=function(a){var b,c=$("li",this._results);a=parseInt(a,10);for(var b=0;b<c.length;b++){if($(c[b]).hasClass(this.selectClass_)){this.focusItem(b+a);return}}this.focusItem(0)};AutoCompleter.prototype.focusItem=function(b){var a,c=$("li",this._results);if(c.length){c.removeClass(this.selectClass_).removeClass(this.options.selectClass);if(typeof b==="number"){b=parseInt(b,10);if(b<0){b=0}else{if(b>=c.length){b=c.length-1}}a=$(c[b])}else{a=$(b)}if(a){a.addClass(this.selectClass_).addClass(this.options.selectClass)}}};AutoCompleter.prototype.selectCurrent=function(){var a=$("li."+this.selectClass_,this._results);if(a.length==1){this.selectItem(a)}else{this.finish()}};AutoCompleter.prototype.selectItem=function(d){var c=d.data("value");var b=d.data("data");var a=this.displayValue(c,b);this.lastProcessedValue_=a;this.lastSelectedValue_=a;this.setValue(a);this.setCaret(a.length);this.callHook("onItemSelect",{value:c,data:b});this.finish()};AutoCompleter.prototype.isContentChar=function(a){if(a.match(this.options.stopCharRegex)){return false}else{if(a===this.options.multipleSeparator){return false}else{return true}}};AutoCompleter.prototype.getValue=function(){var c=this._element.getSelection();var d=this._element.val();var f=c.start;var e=f;for(cpos=f;cpos>=0;cpos=cpos-1){if(cpos===d.length){continue}var b=d.charAt(cpos);if(!this.isContentChar(b)){break}e=cpos}var a=f;for(cpos=f;cpos<d.length;cpos=cpos+1){if(cpos===0){continue}var b=d.charAt(cpos);if(!this.isContentChar(b)){break}a=cpos}this._selection_start=e;this._selection_end=a;return d.substring(e,a)};AutoCompleter.prototype.setValue=function(b){var a=this._element.val().substring(0,this._selection_start);var c=this._element.val().substring(this._selection_end+1);this._element.val(a+b+c)};AutoCompleter.prototype.displayValue=function(b,a){if($.isFunction(this.options.displayValue)){return this.options.displayValue(b,a)}else{return b}};AutoCompleter.prototype.finish=function(){if(this.keyTimeout_){clearTimeout(this.keyTimeout_)}if(this._element.val()!==this.lastSelectedValue_){if(this.options.mustMatch){this._element.val("")}this.callHook("onNoMatch")}this._results.hide();this.lastKeyPressed_=null;this.lastProcessedValue_=null;if(this.active_){this.callHook("onFinish")}this.active_=false};AutoCompleter.prototype.selectRange=function(d,a){var c=this._element.get(0);if(c.setSelectionRange){c.focus();c.setSelectionRange(d,a)}else{if(this.createTextRange){var b=this.createTextRange();b.collapse(true);b.moveEnd("character",a);b.moveStart("character",d);b.select()}}};AutoCompleter.prototype.setCaret=function(a){this.selectRange(a,a)}; (function($){function isRGBACapable(){var $script=$("script:first"),color=$script.css("color"),result=false;if(/^rgba/.test(color)){result=true}else{try{result=(color!=$script.css("color","rgba(0, 0, 0, 0.5)").css("color"));$script.css("color",color)}catch(e){}}return result}$.extend(true,$,{support:{rgba:isRGBACapable()}});var properties=["color","backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","outlineColor"];$.each(properties,function(i,property){$.fx.step[property]=function(fx){if(!fx.init){fx.begin=parseColor($(fx.elem).css(property));fx.end=parseColor(fx.end);fx.init=true}fx.elem.style[property]=calculateColor(fx.begin,fx.end,fx.pos)}});$.fx.step.borderColor=function(fx){if(!fx.init){fx.end=parseColor(fx.end)}var borders=properties.slice(2,6);$.each(borders,function(i,property){if(!fx.init){fx[property]={begin:parseColor($(fx.elem).css(property))}}fx.elem.style[property]=calculateColor(fx[property].begin,fx.end,fx.pos)});fx.init=true};function calculateColor(begin,end,pos){var color="rgb"+($.support.rgba?"a":"")+"("+parseInt((begin[0]+pos*(end[0]-begin[0])),10)+","+parseInt((begin[1]+pos*(end[1]-begin[1])),10)+","+parseInt((begin[2]+pos*(end[2]-begin[2])),10);if($.support.rgba){color+=","+(begin&&end?parseFloat(begin[3]+pos*(end[3]-begin[3])):1)}color+=")";return color}function parseColor(color){var match,triplet;if(match=/#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/.exec(color)){triplet=[parseInt(match[1],16),parseInt(match[2],16),parseInt(match[3],16),1]}else{if(match=/#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/.exec(color)){triplet=[parseInt(match[1],16)*17,parseInt(match[2],16)*17,parseInt(match[3],16)*17,1]}else{if(match=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)){triplet=[parseInt(match[1]),parseInt(match[2]),parseInt(match[3]),1]}else{if(match=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9\.]*)\s*\)/.exec(color)){triplet=[parseInt(match[1],10),parseInt(match[2],10),parseInt(match[3],10),parseFloat(match[4])]}else{if(color=="transparent"){triplet=[0,0,0,0]}}}}}return triplet}})(jQuery); + +/** + * Timeago is a jQuery plugin that makes it easy to support automatically + * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago"). + * + * @name timeago + * @version 0.11.1 + * @requires jQuery v1.2.3+ + * @author Ryan McGeary + * @license MIT License - http://www.opensource.org/licenses/mit-license.php + * + * For usage and examples, visit: + * http://timeago.yarp.com/ + * + * Copyright (c) 2008-2011, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org) + */ +(function($) { + $.timeago = function(timestamp) { + if (timestamp instanceof Date) { + return inWords(timestamp); + } else if (typeof timestamp === "string") { + return inWords($.timeago.parse(timestamp)); + } else { + return inWords($.timeago.datetime(timestamp)); + } + }; + var $t = $.timeago; + + $.extend($.timeago, { + settings: { + refreshMillis: 60000, + allowFuture: false, + strings: { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: gettext("ago"), + suffixFromNow: gettext("from now"), + seconds: gettext("just now"), + minute: gettext("about a minute"), + minutes: gettext("%d minutes"), + hour: gettext("about an hour"), + hours: gettext("%d hours"), + day: gettext("yesterday"), + days: gettext("%d days"), + month: gettext("about a month"), + months: gettext("%d months"), + year: gettext("about a year"), + years: gettext("%d years"), + wordSeparator: " ", + numbers: [] + } + }, + inWords: function(distanceMillis) { + var $l = this.settings.strings; + var prefix = $l.prefixAgo; + var suffix = $l.suffixAgo; + if (this.settings.allowFuture) { + if (distanceMillis < 0) { + prefix = $l.prefixFromNow; + suffix = $l.suffixFromNow; + } + } + + var seconds = Math.abs(distanceMillis) / 1000; + var minutes = seconds / 60; + var hours = minutes / 60; + var days = hours / 24; + var years = days / 365; + + function substitute(stringOrFunction, number) { + var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction; + var value = ($l.numbers && $l.numbers[number]) || number; + return string.replace(/%d/i, value); + } + + var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) || + seconds < 90 && substitute($l.minute, 1) || + minutes < 45 && substitute($l.minutes, Math.round(minutes)) || + minutes < 90 && substitute($l.hour, 1) || + hours < 24 && substitute($l.hours, Math.round(hours)) || + hours < 42 && substitute($l.day, 1) || + days < 30 && substitute($l.days, Math.round(days)) || + days < 45 && substitute($l.month, 1) || + days < 365 && substitute($l.months, Math.round(days / 30)) || + years < 1.5 && substitute($l.year, 1) || + substitute($l.years, Math.round(years)); + + var separator = $l.wordSeparator === undefined ? " " : $l.wordSeparator; + return $.trim([prefix, words, suffix].join(separator)); + }, + parse: function(iso8601) { + var s = $.trim(iso8601); + s = s.replace(/\.\d\d\d+/,""); // remove milliseconds + s = s.replace(/-/,"/").replace(/-/,"/"); + s = s.replace(/T/," ").replace(/Z/," UTC"); + s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400 + return new Date(s); + }, + datetime: function(elem) { + // jQuery's `is()` doesn't play well with HTML5 in IE + var isTime = $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time"); + var iso8601 = isTime ? $(elem).attr("datetime") : $(elem).attr("title"); + return $t.parse(iso8601); + } + }); + + $.fn.timeago = function() { + var self = this; + self.each(refresh); + + var $s = $t.settings; + if ($s.refreshMillis > 0) { + setInterval(function() { self.each(refresh); }, $s.refreshMillis); + } + return self; + }; + + function refresh() { + var data = prepareData(this); + if (!isNaN(data.datetime)) { + $(this).text(inWords(data.datetime)); + } + return this; + } + + function prepareData(element) { + element = $(element); + if (!element.data("timeago")) { + element.data("timeago", { datetime: $t.datetime(element) }); + var text = $.trim(element.text()); + if (text.length > 0) { + element.attr("title", text); + } + } + return element.data("timeago"); + } + + function inWords(date) { + var distanceMillis = distance(date); + var seconds = Math.abs(distanceMillis) / 1000; + var minutes = seconds / 60; + var hours = minutes / 60; + var days = hours / 24; + var years = days / 365; + var months = [ + gettext('Jan'), + gettext('Feb'), + gettext('Mar'), + gettext('Apr'), + gettext('May'), + gettext('Jun'), + gettext('Jul'), + gettext('Aug'), + gettext('Sep'), + gettext('Oct'), + gettext('Nov'), + gettext('Dec') + ]; + //todo: rewrite this in javascript + if (days > 2){ + var month_date = months[date.getMonth()] + ' ' + date.getDate() + if (years == 0){ + //how to do this in js??? + return month_date; + } else { + return month_date + ' ' + "'" + date.getYear() % 20; + } + } else if (days == 2) { + return gettext('2 days ago') + } else if (days == 1) { + return gettext('yesterday') + } else if (minutes >= 60) { + return interpolate( + ngettext( + '%s hour ago', + '%s hours ago', + hours + ), + [Math.floor(hours),] + ) + } else if (seconds > 90){ + return interpolate( + ngettext( + '%s min ago', + '%s mins ago', + minutes + ), + [Math.floor(minutes),] + ) + } else { + return gettext('just now') + } + } + + function distance(date) { + return (new Date() - date); + } + + // fix for IE6 suckage + document.createElement("abbr"); + document.createElement("time"); +}(jQuery)); diff --git a/askbot/skins/common/media/js/wmd/wmd.css b/askbot/skins/common/media/js/wmd/wmd.css index 3ad615e7..678d70f3 100644 --- a/askbot/skins/common/media/js/wmd/wmd.css +++ b/askbot/skins/common/media/js/wmd/wmd.css @@ -10,12 +10,9 @@ #wmd-button-bar { background: url(images/editor-toolbar-background.png) repeat-x bottom; - height:36px; - border-left:#cce6ec 3px solid; - border-top:#cce6ec 3px solid; - border-right:#cce6ec 3px solid; - float:left; - width:730px; + height: 30px; + border: 0; + display: block; } #wmd-input @@ -39,10 +36,7 @@ #wmd-button-row { position: relative; - margin-left: 5px; - margin-right: 5px; - margin-bottom: 0px; - margin-top: 10px; + margin: 10px 2px 0 2px; padding: 0px; height: 20px; } @@ -51,9 +45,9 @@ { width: 1px; height: 20px; - margin-left: 14px; - - position: absolute; + margin-left: 2px; + margin-right: 4px; + /*position: absolute;*/ background-color: Silver; display: inline-block; list-style: none; @@ -66,7 +60,7 @@ margin-left: 5px; margin-right: 5px; - position: absolute; + /*position: absolute;*/ background-image: url(images/wmd-buttons.png); background-repeat: no-repeat; background-position: 0px 0px; diff --git a/askbot/skins/common/media/js/wmd/wmd.js b/askbot/skins/common/media/js/wmd/wmd.js index 1d524361..98af264f 100644 --- a/askbot/skins/common/media/js/wmd/wmd.js +++ b/askbot/skins/common/media/js/wmd/wmd.js @@ -69,7 +69,7 @@ Attacklab.wmdBase = function(){ var helpLink = "http://wmd-editor.com/"; var helpHoverTitle = "WMD website"; var helpTarget = "_blank"; - var localUploadFileName = null; + var localUploadFileName = null; // ------------------------------------------------------------------- // END OF YOUR CHANGES @@ -346,6 +346,9 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ style.marginLeft = style.marginRight = "auto"; form.appendChild(input); + //EF. fucus at the end of the input box + //putCursorAtEnd($(input)); + // The upload file input if(dialogType == 'image' || dialogType == 'file'){ var upload_container = $('<div></div>'); @@ -846,6 +849,11 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ var creationHandle; var undoMgr; // The undo manager + + var isButtonUsed = function(button){ + var buttons = $.trim(wmd.wmd_env.buttons).split(/\s+/); + return $.inArray(button, buttons) !== -1; + }; // Perform the button's action. var doClick = function(button){ @@ -968,159 +976,198 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ buttonRow.id = "wmd-button-row"; buttonRow = buttonBar.appendChild(buttonRow); + if (isButtonUsed('bold')){ + var boldButton = document.createElement("li"); + boldButton.className = "wmd-button"; + boldButton.id = "wmd-bold-button"; + boldButton.title = toolbar_strong_label; + boldButton.XShift = "0px"; + boldButton.textOp = command.doBold; + setupButton(boldButton, true); + buttonRow.appendChild(boldButton); + } - var boldButton = document.createElement("li"); - boldButton.className = "wmd-button"; - boldButton.id = "wmd-bold-button"; - boldButton.title = toolbar_strong_label; - boldButton.XShift = "0px"; - boldButton.textOp = command.doBold; - setupButton(boldButton, true); - buttonRow.appendChild(boldButton); - - var italicButton = document.createElement("li"); - italicButton.className = "wmd-button"; - italicButton.id = "wmd-italic-button"; - italicButton.title = toolbar_emphasis_label; - italicButton.XShift = "-20px"; - italicButton.textOp = command.doItalic; - setupButton(italicButton, true); - buttonRow.appendChild(italicButton); + if (isButtonUsed('italic')){ + var italicButton = document.createElement("li"); + italicButton.className = "wmd-button"; + italicButton.id = "wmd-italic-button"; + italicButton.title = toolbar_emphasis_label; + italicButton.XShift = "-20px"; + italicButton.textOp = command.doItalic; + setupButton(italicButton, true); + buttonRow.appendChild(italicButton); + } - var spacer1 = document.createElement("li"); - spacer1.className = "wmd-spacer"; - spacer1.id = "wmd-spacer1"; - buttonRow.appendChild(spacer1); + if ( + isButtonUsed('link') || + isButtonUsed('blockquote') || + isButtonUsed('code') || + isButtonUsed('image') || + isButtonUsed('attachment') + ) { + var spacer1 = document.createElement("li"); + spacer1.className = "wmd-spacer"; + spacer1.id = "wmd-spacer1"; + buttonRow.appendChild(spacer1); + } - var linkButton = document.createElement("li"); - linkButton.className = "wmd-button"; - linkButton.id = "wmd-link-button"; - linkButton.title = toolbar_hyperlink_label; - linkButton.XShift = "-40px"; - linkButton.textOp = function(chunk, postProcessing){ - return command.doLinkOrImage(chunk, postProcessing, 'link'); - }; - setupButton(linkButton, true); - buttonRow.appendChild(linkButton); + if (isButtonUsed('link')){ + var linkButton = document.createElement("li"); + linkButton.className = "wmd-button"; + linkButton.id = "wmd-link-button"; + linkButton.title = toolbar_hyperlink_label; + linkButton.XShift = "-40px"; + linkButton.textOp = function(chunk, postProcessing){ + return command.doLinkOrImage(chunk, postProcessing, 'link'); + }; + setupButton(linkButton, true); + buttonRow.appendChild(linkButton); + } - var quoteButton = document.createElement("li"); - quoteButton.className = "wmd-button"; - quoteButton.id = "wmd-quote-button"; - quoteButton.title = toolbar_blockquote_label; - quoteButton.XShift = "-60px"; - quoteButton.textOp = command.doBlockquote; - setupButton(quoteButton, true); - buttonRow.appendChild(quoteButton); - - var codeButton = document.createElement("li"); - codeButton.className = "wmd-button"; - codeButton.id = "wmd-code-button"; - codeButton.title = toolbar_code_label; - codeButton.XShift = "-80px"; - codeButton.textOp = command.doCode; - setupButton(codeButton, true); - buttonRow.appendChild(codeButton); + if (isButtonUsed('blockquote')){ + var quoteButton = document.createElement("li"); + quoteButton.className = "wmd-button"; + quoteButton.id = "wmd-quote-button"; + quoteButton.title = toolbar_blockquote_label; + quoteButton.XShift = "-60px"; + quoteButton.textOp = command.doBlockquote; + setupButton(quoteButton, true); + buttonRow.appendChild(quoteButton); + } + + if (isButtonUsed('code')){ + var codeButton = document.createElement("li"); + codeButton.className = "wmd-button"; + codeButton.id = "wmd-code-button"; + codeButton.title = toolbar_code_label; + codeButton.XShift = "-80px"; + codeButton.textOp = command.doCode; + setupButton(codeButton, true); + buttonRow.appendChild(codeButton); + } - var imageButton = document.createElement("li"); - imageButton.className = "wmd-button"; - imageButton.id = "wmd-image-button"; - imageButton.title = toolbar_image_label; - imageButton.XShift = "-100px"; - imageButton.textOp = function(chunk, postProcessing){ - return command.doLinkOrImage(chunk, postProcessing, 'image'); - }; - setupButton(imageButton, true); - buttonRow.appendChild(imageButton); + if (isButtonUsed('image')){ + var imageButton = document.createElement("li"); + imageButton.className = "wmd-button"; + imageButton.id = "wmd-image-button"; + imageButton.title = toolbar_image_label; + imageButton.XShift = "-100px"; + imageButton.textOp = function(chunk, postProcessing){ + return command.doLinkOrImage(chunk, postProcessing, 'image'); + }; + setupButton(imageButton, true); + buttonRow.appendChild(imageButton); + } - var attachmentButton = document.createElement("li"); - attachmentButton.className = "wmd-button"; - attachmentButton.id = "wmd-attachment-button"; - attachmentButton.title = toolbar_attachment_label; - attachmentButton.XShift = "-120px"; - attachmentButton.textOp = function(chunk, postProcessing){ - return command.doLinkOrImage(chunk, postProcessing, 'file'); - }; - setupButton(attachmentButton, true); - buttonRow.appendChild(attachmentButton); + if (isButtonUsed('attachment')){ + var attachmentButton = document.createElement("li"); + attachmentButton.className = "wmd-button"; + attachmentButton.id = "wmd-attachment-button"; + attachmentButton.title = toolbar_attachment_label; + attachmentButton.XShift = "-120px"; + attachmentButton.textOp = function(chunk, postProcessing){ + return command.doLinkOrImage(chunk, postProcessing, 'file'); + }; + setupButton(attachmentButton, true); + buttonRow.appendChild(attachmentButton); + } - var spacer2 = document.createElement("li"); - spacer2.className = "wmd-spacer"; - spacer2.id = "wmd-spacer2"; - buttonRow.appendChild(spacer2); + if ( + isButtonUsed('ol') || + isButtonUsed('ul') || + isButtonUsed('heading') || + isButtonUsed('hr') + ) { + var spacer2 = document.createElement("li"); + spacer2.className = "wmd-spacer"; + spacer2.id = "wmd-spacer2"; + buttonRow.appendChild(spacer2); + } - var olistButton = document.createElement("li"); - olistButton.className = "wmd-button"; - olistButton.id = "wmd-olist-button"; - olistButton.title = toolbar_numbered_label; - olistButton.XShift = "-140px"; - olistButton.textOp = function(chunk, postProcessing){ - command.doList(chunk, postProcessing, true); - }; - setupButton(olistButton, true); - buttonRow.appendChild(olistButton); - - var ulistButton = document.createElement("li"); - ulistButton.className = "wmd-button"; - ulistButton.id = "wmd-ulist-button"; - ulistButton.title = toolbar_bulleted_label; - ulistButton.XShift = "-160px"; - ulistButton.textOp = function(chunk, postProcessing){ - command.doList(chunk, postProcessing, false); - }; - setupButton(ulistButton, true); - buttonRow.appendChild(ulistButton); - - var headingButton = document.createElement("li"); - headingButton.className = "wmd-button"; - headingButton.id = "wmd-heading-button"; - headingButton.title = toolbar_heading_label; - headingButton.XShift = "-180px"; - headingButton.textOp = command.doHeading; - setupButton(headingButton, true); - buttonRow.appendChild(headingButton); - - var hrButton = document.createElement("li"); - hrButton.className = "wmd-button"; - hrButton.id = "wmd-hr-button"; - hrButton.title = toolbar_horizontal_label; - hrButton.XShift = "-200px"; - hrButton.textOp = command.doHorizontalRule; - setupButton(hrButton, true); - buttonRow.appendChild(hrButton); - - var spacer3 = document.createElement("li"); - spacer3.className = "wmd-spacer"; - spacer3.id = "wmd-spacer3"; - buttonRow.appendChild(spacer3); - - var undoButton = document.createElement("li"); - undoButton.className = "wmd-button"; - undoButton.id = "wmd-undo-button"; - undoButton.title = toolbar_undo_label; - undoButton.XShift = "-220px"; - undoButton.execute = function(manager){ - manager.undo(); - }; - setupButton(undoButton, true); - buttonRow.appendChild(undoButton); + if (isButtonUsed('ol')) { + var olistButton = document.createElement("li"); + olistButton.className = "wmd-button"; + olistButton.id = "wmd-olist-button"; + olistButton.title = toolbar_numbered_label; + olistButton.XShift = "-140px"; + olistButton.textOp = function(chunk, postProcessing){ + command.doList(chunk, postProcessing, true); + }; + setupButton(olistButton, true); + buttonRow.appendChild(olistButton); + } - var redoButton = document.createElement("li"); - redoButton.className = "wmd-button"; - redoButton.id = "wmd-redo-button"; - redoButton.title = toolbar_redo_label; - if (/win/.test(nav.platform.toLowerCase())) { - redoButton.title = toolbar_redo_label; - } - else { - // mac and other non-Windows platforms - redoButton.title = gettext('redo') + " - Ctrl+Shift+Z"; - } - redoButton.XShift = "-240px"; - redoButton.execute = function(manager){ - manager.redo(); - }; - setupButton(redoButton, true); - buttonRow.appendChild(redoButton); + if (isButtonUsed('ul')) { + var ulistButton = document.createElement("li"); + ulistButton.className = "wmd-button"; + ulistButton.id = "wmd-ulist-button"; + ulistButton.title = toolbar_bulleted_label; + ulistButton.XShift = "-160px"; + ulistButton.textOp = function(chunk, postProcessing){ + command.doList(chunk, postProcessing, false); + }; + setupButton(ulistButton, true); + buttonRow.appendChild(ulistButton); + } + + if (isButtonUsed('heading')) { + var headingButton = document.createElement("li"); + headingButton.className = "wmd-button"; + headingButton.id = "wmd-heading-button"; + headingButton.title = toolbar_heading_label; + headingButton.XShift = "-180px"; + headingButton.textOp = command.doHeading; + setupButton(headingButton, true); + buttonRow.appendChild(headingButton); + } + + if (isButtonUsed('hr')) { + var hrButton = document.createElement("li"); + hrButton.className = "wmd-button"; + hrButton.id = "wmd-hr-button"; + hrButton.title = toolbar_horizontal_label; + hrButton.XShift = "-200px"; + hrButton.textOp = command.doHorizontalRule; + setupButton(hrButton, true); + buttonRow.appendChild(hrButton); + } + + if (isButtonUsed('undo')){ + var spacer3 = document.createElement("li"); + spacer3.className = "wmd-spacer"; + spacer3.id = "wmd-spacer3"; + buttonRow.appendChild(spacer3); + + var undoButton = document.createElement("li"); + undoButton.className = "wmd-button"; + undoButton.id = "wmd-undo-button"; + undoButton.title = toolbar_undo_label; + undoButton.XShift = "-220px"; + undoButton.execute = function(manager){ + manager.undo(); + }; + setupButton(undoButton, true); + buttonRow.appendChild(undoButton); + + var redoButton = document.createElement("li"); + redoButton.className = "wmd-button"; + redoButton.id = "wmd-redo-button"; + redoButton.title = toolbar_redo_label; + if (/win/.test(nav.platform.toLowerCase())) { + redoButton.title = toolbar_redo_label; + } + else { + // mac and other non-Windows platforms + redoButton.title = gettext('redo') + " - Ctrl+Shift+Z"; + } + redoButton.XShift = "-240px"; + redoButton.execute = function(manager){ + manager.redo(); + }; + setupButton(redoButton, true); + buttonRow.appendChild(redoButton); + setUndoRedoButtonStates(); + } /* var helpButton = document.createElement("li"); helpButton.className = "wmd-button"; @@ -1137,7 +1184,6 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ setupButton(helpButton, true); buttonRow.appendChild(helpButton); */ - setUndoRedoButtonStates(); }; var setupEditor = function(){ @@ -1146,7 +1192,7 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ wmd.nativeUndo = true; } - if (!wmd.nativeUndo) { + if (!wmd.nativeUndo && isButtonUsed('undo')) { undoMgr = new wmd.undoManager(function(){ previewRefreshCallback(); setUndoRedoButtonStates(); @@ -1814,13 +1860,17 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ wmd.wmd.previewManager = wmd.previewManager; }; - util.startEditor = function(){ + util.startEditor = function(start_now, buttons){ if (wmd.wmd_env.autostart === false) { util.makeAPI(); return; } + if (buttons){ + wmd.wmd_env.buttons = buttons; + } + var edit; // The editor (buttons + input + outputs) - the main object. var previewMgr; // The preview manager. @@ -1838,7 +1888,11 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){ }; - util.addEvent(top, "load", loadListener); + if (start_now){ + loadListener(); + } else { + util.addEvent(top, "load", loadListener); + } }; wmd.previewManager = function(){ @@ -2424,7 +2478,7 @@ if(!Attacklab.wmd) mergeEnv(top["wmd_options"]); Attacklab.full = true; - var defaultButtons = "bold italic link blockquote code image ol ul heading hr"; + var defaultButtons = "bold italic link blockquote code image attachment ol ul heading hr"; Attacklab.wmd_env.buttons = Attacklab.wmd_env.buttons || defaultButtons; }; Attacklab.loadEnv(); diff --git a/askbot/skins/common/templates/authopenid/signin.html b/askbot/skins/common/templates/authopenid/signin.html index e849db20..f9c0cfea 100644 --- a/askbot/skins/common/templates/authopenid/signin.html +++ b/askbot/skins/common/templates/authopenid/signin.html @@ -1,5 +1,6 @@ {% extends "two_column_body.html" %}
{% import "authopenid/authopenid_macros.html" as login_macros %}
+{% from "macros.html" import timeago %}
<!-- signin.html -->
{% block title %}{% spaceless %}{% trans %}User login{% endtrans %}{% endspaceless %}{% endblock %}
{% block forestyle %}
@@ -169,7 +170,7 @@ </td>
<td>
{% if login_method.last_used_timestamp %}
- {{login_method.last_used_timestamp|diff_date}}
+ {{ timeago(login_method.last_used_timestamp) }}
{% endif %}
</td>
<td>
diff --git a/askbot/skins/common/templates/authopenid/signup_with_password.html b/askbot/skins/common/templates/authopenid/signup_with_password.html index e79263d2..e65cd518 100644 --- a/askbot/skins/common/templates/authopenid/signup_with_password.html +++ b/askbot/skins/common/templates/authopenid/signup_with_password.html @@ -22,12 +22,12 @@ </form> <h2>{% trans %}or create a new user name and password here{% endtrans %}</h2> {% else %} - <h1>{% trans %}Create login name and password{% endtrans %}</h1> - <p class="message">{% trans %}<span class='strong big'>If you prefer, create your forum login name and + <h1 class="section-title">{% trans %}Create login name and password{% endtrans %}</h1> + <!--p class="message">{% trans %}<span class='strong big'>If you prefer, create your forum login name and password here. However</span>, please keep in mind that we also support <strong>OpenID</strong> login method. With <strong>OpenID</strong> you can simply reuse your external login (e.g. Gmail or AOL) without ever sharing -your login details with anyone and having to remember yet another password.{% endtrans %} +your login details with anyone and having to remember yet another password.{% endtrans %}</p--> {%endif%} <form action="{% url user_signup_with_password %}" method="post" accept-charset="utf-8">{% csrf_token %} {{form.login_provider}} diff --git a/askbot/skins/common/templates/question/answer_author_info.html b/askbot/skins/common/templates/question/answer_author_info.html index f17cb62c..1c729b51 100644 --- a/askbot/skins/common/templates/question/answer_author_info.html +++ b/askbot/skins/common/templates/question/answer_author_info.html @@ -1,6 +1,8 @@ {{ macros.post_last_updater_and_creator_info( answer, - settings.MIN_REP_TO_EDIT_WIKI + settings.MIN_REP_TO_EDIT_WIKI, + karma_mode = settings.KARMA_MODE, + badges_mode = settings.BADGES_MODE ) }} diff --git a/askbot/skins/common/templates/question/question_author_info.html b/askbot/skins/common/templates/question/question_author_info.html index e43f8931..c25b7d84 100644 --- a/askbot/skins/common/templates/question/question_author_info.html +++ b/askbot/skins/common/templates/question/question_author_info.html @@ -1,6 +1,8 @@ {{ macros.post_last_updater_and_creator_info( question, - settings.MIN_REP_TO_EDIT_WIKI + settings.MIN_REP_TO_EDIT_WIKI, + karma_mode = settings.KARMA_MODE, + badges_mode = settings.BADGES_MODE ) }} diff --git a/askbot/skins/common/templates/widgets/edit_post.html b/askbot/skins/common/templates/widgets/edit_post.html index ee84f443..66f79237 100644 --- a/askbot/skins/common/templates/widgets/edit_post.html +++ b/askbot/skins/common/templates/widgets/edit_post.html @@ -7,9 +7,11 @@ </div> </div> {% endif %} -<div id="wmd-button-bar" class="wmd-panel"></div> -<div class="form-item"> +<div class='wmd-container'> + <div id="wmd-button-bar" class="wmd-panel"></div> {{ post_form.text }}{# this element is resizable and will be wrapped by js #} +</div> +<div class="form-item"> <label for="editor" class="form-error">{{ post_form.text.errors }}</label> </div> {# need label element for resizable input, b/c form validation won't find span #} diff --git a/askbot/skins/common/templates/widgets/tag_selector.html b/askbot/skins/common/templates/widgets/tag_selector.html index ff298488..2413a9c3 100644 --- a/askbot/skins/common/templates/widgets/tag_selector.html +++ b/askbot/skins/common/templates/widgets/tag_selector.html @@ -35,14 +35,45 @@ <input id="ignoredTagInput" autocomplete="off" type="text"/> <input id="ignoredTagAdd" type="submit" value="{% trans %}add{% endtrans%}"/> </div> + {% if settings.SUBSCRIBED_TAG_SELECTOR_ENABLED %} + <h2>{% trans %}Subscribed tags{% endtrans %}</h2> + {{ + macros.tag_list_widget( + subscribed_tag_names, + deletable = True, + css_class = 'subscribed marked-tags', + search_state = search_state + ) + }} + {# todo: add this via javascript + "remove '%(tag_name)s' from the list of ignored tags"| + format(tag_name = tag_name) + #} + <div class="inputs"> + <input id="subscribedTagInput" autocomplete="off" type="text"/> + <input id="subscribedTagAdd" type="submit" value="{% trans %}add{% endtrans%}"/> + </div> + {% endif %} <h3>{% trans %}Display tag filter{% endtrans%}</h3> <div id="displayTagFilterControl"> {{ macros.radio_select( name = "display_tag_filter_strategy", value = request.user.display_tag_filter_strategy, - choices = tag_filter_strategy_choices + choices = display_tag_filter_strategy_choices ) }} </div> + {% if settings.SUBSCRIBED_TAG_SELECTOR_ENABLED %} + <h3>{% trans %}Email tag filter{% endtrans%}</h3> + <div id="emailTagFilterControl"> + {{ + macros.radio_select( + name = "email_tag_filter_strategy", + value = request.user.email_tag_filter_strategy, + choices = email_tag_filter_strategy_choices + ) + }} + </div> + {% endif %} </div> diff --git a/askbot/skins/default/media/bootstrap/bootstrap.zip b/askbot/skins/default/media/bootstrap/bootstrap.zip Binary files differnew file mode 100644 index 00000000..86a13bb9 --- /dev/null +++ b/askbot/skins/default/media/bootstrap/bootstrap.zip diff --git a/askbot/skins/default/media/bootstrap/css/bootstrap.css b/askbot/skins/default/media/bootstrap/css/bootstrap.css new file mode 100644 index 00000000..9447a9a2 --- /dev/null +++ b/askbot/skins/default/media/bootstrap/css/bootstrap.css @@ -0,0 +1,4647 @@ +/*! + * Bootstrap v2.0.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + display: table; + content: ""; +} +.clearfix:after { + clear: both; +} +.hide-text { + overflow: hidden; + text-indent: 100%; + white-space: nowrap; +} +.input-block-level { + display: block; + width: 100%; + min-height: 28px; + /* Make inputs at least the height of their button counterpart */ + + /* Makes inputs behave like true block-level elements */ + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} +audio:not([controls]) { + display: none; +} +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +a:hover, +a:active { + outline: 0; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + height: auto; + border: 0; + -ms-interpolation-mode: bicubic; + vertical-align: middle; +} +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} +button, +input { + *overflow: visible; + line-height: normal; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} +textarea { + overflow: auto; + vertical-align: top; +} +body { + margin: 0; + font-family: Arial, sans-serif; + font-size: 13px; + line-height: 18px; + color: #333333; + background-color: #ffffff; +} +a { + color: #0088cc; + text-decoration: none; +} +a:hover { + color: #005580; + text-decoration: underline; +} +.row { + margin-left: -20px; + *zoom: 1; +} +.row:before, +.row:after { + display: table; + content: ""; +} +.row:after { + clear: both; +} +[class*="span"] { + float: left; + margin-left: 20px; +} +.container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.span12 { + width: 940px; +} +.span11 { + width: 860px; +} +.span10 { + width: 780px; +} +.span9 { + width: 700px; +} +.span8 { + width: 620px; +} +.span7 { + width: 540px; +} +.span6 { + width: 460px; +} +.span5 { + width: 380px; +} +.span4 { + width: 300px; +} +.span3 { + width: 220px; +} +.span2 { + width: 140px; +} +.span1 { + width: 60px; +} +.offset12 { + margin-left: 980px; +} +.offset11 { + margin-left: 900px; +} +.offset10 { + margin-left: 820px; +} +.offset9 { + margin-left: 740px; +} +.offset8 { + margin-left: 660px; +} +.offset7 { + margin-left: 580px; +} +.offset6 { + margin-left: 500px; +} +.offset5 { + margin-left: 420px; +} +.offset4 { + margin-left: 340px; +} +.offset3 { + margin-left: 260px; +} +.offset2 { + margin-left: 180px; +} +.offset1 { + margin-left: 100px; +} +.row-fluid { + width: 100%; + *zoom: 1; +} +.row-fluid:before, +.row-fluid:after { + display: table; + content: ""; +} +.row-fluid:after { + clear: both; +} +.row-fluid > [class*="span"] { + float: left; + margin-left: 2.127659574%; +} +.row-fluid > [class*="span"]:first-child { + margin-left: 0; +} +.row-fluid > .span12 { + width: 99.99999998999999%; +} +.row-fluid > .span11 { + width: 91.489361693%; +} +.row-fluid > .span10 { + width: 82.97872339599999%; +} +.row-fluid > .span9 { + width: 74.468085099%; +} +.row-fluid > .span8 { + width: 65.95744680199999%; +} +.row-fluid > .span7 { + width: 57.446808505%; +} +.row-fluid > .span6 { + width: 48.93617020799999%; +} +.row-fluid > .span5 { + width: 40.425531911%; +} +.row-fluid > .span4 { + width: 31.914893614%; +} +.row-fluid > .span3 { + width: 23.404255317%; +} +.row-fluid > .span2 { + width: 14.89361702%; +} +.row-fluid > .span1 { + width: 6.382978723%; +} +.container { + margin-left: auto; + margin-right: auto; + *zoom: 1; +} +.container:before, +.container:after { + display: table; + content: ""; +} +.container:after { + clear: both; +} +.container-fluid { + padding-left: 20px; + padding-right: 20px; + *zoom: 1; +} +.container-fluid:before, +.container-fluid:after { + display: table; + content: ""; +} +.container-fluid:after { + clear: both; +} +p { + margin: 0 0 9px; + font-family: Arial, sans-serif; + font-size: 13px; + line-height: 18px; +} +p small { + font-size: 11px; + color: #999999; +} +.lead { + margin-bottom: 18px; + font-size: 20px; + font-weight: 200; + line-height: 27px; +} +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + font-family: inherit; + font-weight: bold; + color: inherit; + text-rendering: optimizelegibility; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + color: #999999; +} +h1 { + font-size: 30px; + line-height: 36px; +} +h1 small { + font-size: 18px; +} +h2 { + font-size: 24px; + line-height: 36px; +} +h2 small { + font-size: 18px; +} +h3 { + line-height: 27px; + font-size: 18px; +} +h3 small { + font-size: 14px; +} +h4, +h5, +h6 { + line-height: 18px; +} +h4 { + font-size: 14px; +} +h4 small { + font-size: 12px; +} +h5 { + font-size: 12px; +} +h6 { + font-size: 11px; + color: #999999; + text-transform: uppercase; +} +.page-header { + padding-bottom: 17px; + margin: 18px 0; + border-bottom: 1px solid #eeeeee; +} +.page-header h1 { + line-height: 1; +} +ul, +ol { + padding: 0; + margin: 0 0 9px 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +ul { + list-style: disc; +} +ol { + list-style: decimal; +} +li { + line-height: 18px; +} +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} +dl { + margin-bottom: 18px; +} +dt, +dd { + line-height: 18px; +} +dt { + font-weight: bold; + line-height: 17px; +} +dd { + margin-left: 9px; +} +.dl-horizontal dt { + float: left; + clear: left; + width: 120px; + text-align: right; +} +.dl-horizontal dd { + margin-left: 130px; +} +hr { + margin: 18px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} +strong { + font-weight: bold; +} +em { + font-style: italic; +} +.muted { + color: #999999; +} +abbr[title] { + border-bottom: 1px dotted #ddd; + cursor: help; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 0 0 0 15px; + margin: 0 0 18px; + border-left: 5px solid #eeeeee; +} +blockquote p { + margin-bottom: 0; + font-size: 16px; + font-weight: 300; + line-height: 22.5px; +} +blockquote small { + display: block; + line-height: 18px; + color: #999999; +} +blockquote small:before { + content: '\2014 \00A0'; +} +blockquote.pull-right { + float: right; + padding-left: 0; + padding-right: 15px; + border-left: 0; + border-right: 5px solid #eeeeee; +} +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} +address { + display: block; + margin-bottom: 18px; + line-height: 18px; + font-style: normal; +} +small { + font-size: 100%; +} +cite { + font-style: normal; +} +code, +pre { + padding: 0 3px 2px; + font-family: Menlo, Monaco, "Courier New", monospace; + font-size: 12px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +code { + padding: 2px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; +} +pre { + display: block; + padding: 8.5px; + margin: 0 0 9px; + font-size: 12.025px; + line-height: 18px; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + white-space: pre; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; +} +pre.prettyprint { + margin-bottom: 18px; +} +pre code { + padding: 0; + color: inherit; + background-color: transparent; + border: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +/*.label { + padding: 1px 4px 2px; + font-size: 10.998px; + font-weight: bold; + line-height: 13px; + color: #ffffff; + vertical-align: middle; + white-space: nowrap; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #999999; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.label:hover { + color: #ffffff; + text-decoration: none; +}*/ +.label-important { + background-color: #b94a48; +} +.label-important:hover { + background-color: #953b39; +} +.label-warning { + background-color: #f89406; +} +.label-warning:hover { + background-color: #c67605; +} +.label-success { + background-color: #468847; +} +.label-success:hover { + background-color: #356635; +} +.label-info { + background-color: #3a87ad; +} +.label-info:hover { + background-color: #2d6987; +} +.label-inverse { + background-color: #333333; +} +.label-inverse:hover { + background-color: #1a1a1a; +} +.badge { + padding: 1px 9px 2px; + font-size: 12.025px; + font-weight: bold; + white-space: nowrap; + color: #ffffff; + background-color: #999999; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} +.badge:hover { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.badge-error { + background-color: #b94a48; +} +.badge-error:hover { + background-color: #953b39; +} +.badge-warning { + background-color: #f89406; +} +.badge-warning:hover { + background-color: #c67605; +} +.badge-success { + background-color: #468847; +} +.badge-success:hover { + background-color: #356635; +} +.badge-info { + background-color: #3a87ad; +} +.badge-info:hover { + background-color: #2d6987; +} +.badge-inverse { + background-color: #333333; +} +.badge-inverse:hover { + background-color: #1a1a1a; +} +table { + max-width: 100%; + border-collapse: collapse; + border-spacing: 0; + background-color: transparent; +} +.table { + width: 100%; + margin-bottom: 18px; +} +.table th, +.table td { + padding: 8px; + line-height: 18px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} +.table th { + font-weight: bold; +} +.table thead th { + vertical-align: bottom; +} +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} +.table tbody + tbody { + border-top: 2px solid #dddddd; +} +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} +.table-bordered { + border: 1px solid #dddddd; + border-left: 0; + border-collapse: separate; + *border-collapse: collapsed; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} +.table-bordered thead:first-child tr:first-child th:first-child, +.table-bordered tbody:first-child tr:first-child td:first-child { + -webkit-border-radius: 4px 0 0 0; + -moz-border-radius: 4px 0 0 0; + border-radius: 4px 0 0 0; +} +.table-bordered thead:first-child tr:first-child th:last-child, +.table-bordered tbody:first-child tr:first-child td:last-child { + -webkit-border-radius: 0 4px 0 0; + -moz-border-radius: 0 4px 0 0; + border-radius: 0 4px 0 0; +} +.table-bordered thead:last-child tr:last-child th:first-child, +.table-bordered tbody:last-child tr:last-child td:first-child { + -webkit-border-radius: 0 0 0 4px; + -moz-border-radius: 0 0 0 4px; + border-radius: 0 0 0 4px; +} +.table-bordered thead:last-child tr:last-child th:last-child, +.table-bordered tbody:last-child tr:last-child td:last-child { + -webkit-border-radius: 0 0 4px 0; + -moz-border-radius: 0 0 4px 0; + border-radius: 0 0 4px 0; +} +.table-striped tbody tr:nth-child(odd) td, +.table-striped tbody tr:nth-child(odd) th { + background-color: #f9f9f9; +} +.table tbody tr:hover td, +.table tbody tr:hover th { + background-color: #f5f5f5; +} +table .span1 { + float: none; + width: 44px; + margin-left: 0; +} +table .span2 { + float: none; + width: 124px; + margin-left: 0; +} +table .span3 { + float: none; + width: 204px; + margin-left: 0; +} +table .span4 { + float: none; + width: 284px; + margin-left: 0; +} +table .span5 { + float: none; + width: 364px; + margin-left: 0; +} +table .span6 { + float: none; + width: 444px; + margin-left: 0; +} +table .span7 { + float: none; + width: 524px; + margin-left: 0; +} +table .span8 { + float: none; + width: 604px; + margin-left: 0; +} +table .span9 { + float: none; + width: 684px; + margin-left: 0; +} +table .span10 { + float: none; + width: 764px; + margin-left: 0; +} +table .span11 { + float: none; + width: 844px; + margin-left: 0; +} +table .span12 { + float: none; + width: 924px; + margin-left: 0; +} +table .span13 { + float: none; + width: 1004px; + margin-left: 0; +} +table .span14 { + float: none; + width: 1084px; + margin-left: 0; +} +table .span15 { + float: none; + width: 1164px; + margin-left: 0; +} +table .span16 { + float: none; + width: 1244px; + margin-left: 0; +} +table .span17 { + float: none; + width: 1324px; + margin-left: 0; +} +table .span18 { + float: none; + width: 1404px; + margin-left: 0; +} +table .span19 { + float: none; + width: 1484px; + margin-left: 0; +} +table .span20 { + float: none; + width: 1564px; + margin-left: 0; +} +table .span21 { + float: none; + width: 1644px; + margin-left: 0; +} +table .span22 { + float: none; + width: 1724px; + margin-left: 0; +} +table .span23 { + float: none; + width: 1804px; + margin-left: 0; +} +table .span24 { + float: none; + width: 1884px; + margin-left: 0; +} +form { + margin: 0 0 18px; +} +fieldset { + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 27px; + font-size: 19.5px; + line-height: 36px; + color: #333333; + border: 0; + border-bottom: 1px solid #eee; +} +legend small { + font-size: 13.5px; + color: #999999; +} +label, +input, +button, +select, +textarea { + font-size: 13px; + font-weight: normal; + line-height: 18px; +} +input, +button, +select, +textarea { + font-family: Arial, sans-serif; +} +label { + display: block; + margin-bottom: 5px; + color: #333333; +} +input, +textarea, +select, +.uneditable-input { + display: inline-block; + width: 210px; + height: 18px; + padding: 4px; + margin-bottom: 9px; + font-size: 13px; + line-height: 18px; + color: #555555; + border: 1px solid #cccccc; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.uneditable-textarea { + width: auto; + height: auto; +} +label input, +label textarea, +label select { + display: block; +} +input[type="image"], +input[type="checkbox"], +input[type="radio"] { + width: auto; + height: auto; + padding: 0; + margin: 3px 0; + *margin-top: 0; + /* IE7 */ + + line-height: normal; + cursor: pointer; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + border: 0 \9; + /* IE9 and down */ + +} +input[type="image"] { + border: 0; +} +input[type="file"] { + width: auto; + padding: initial; + line-height: initial; + border: initial; + background-color: #ffffff; + background-color: initial; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +input[type="button"], +input[type="reset"], +input[type="submit"] { + width: auto; + height: auto; +} +select, +input[type="file"] { + height: 28px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + + line-height: 28px; +} +input[type="file"] { + line-height: 18px \9; +} +select { + width: 220px; + background-color: #ffffff; +} +select[multiple], +select[size] { + height: auto; +} +input[type="image"] { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +textarea { + height: auto; +} +input[type="hidden"] { + display: none; +} +.radio, +.checkbox { + padding-left: 18px; +} +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -18px; +} +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} +input, +textarea { + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + -ms-transition: border linear 0.2s, box-shadow linear 0.2s; + -o-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; +} +input:focus, +textarea:focus { + border-color: rgba(82, 168, 236, 0.8); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus, +select:focus { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.input-mini { + width: 60px; +} +.input-small { + width: 90px; +} +.input-medium { + width: 150px; +} +.input-large { + width: 210px; +} +.input-xlarge { + width: 270px; +} +.input-xxlarge { + width: 530px; +} +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input { + float: none; + margin-left: 0; +} +input, +textarea, +.uneditable-input { + margin-left: 0; +} +input.span12, textarea.span12, .uneditable-input.span12 { + width: 930px; +} +input.span11, textarea.span11, .uneditable-input.span11 { + width: 850px; +} +input.span10, textarea.span10, .uneditable-input.span10 { + width: 770px; +} +input.span9, textarea.span9, .uneditable-input.span9 { + width: 690px; +} +input.span8, textarea.span8, .uneditable-input.span8 { + width: 610px; +} +input.span7, textarea.span7, .uneditable-input.span7 { + width: 530px; +} +input.span6, textarea.span6, .uneditable-input.span6 { + width: 450px; +} +input.span5, textarea.span5, .uneditable-input.span5 { + width: 370px; +} +input.span4, textarea.span4, .uneditable-input.span4 { + width: 290px; +} +input.span3, textarea.span3, .uneditable-input.span3 { + width: 210px; +} +input.span2, textarea.span2, .uneditable-input.span2 { + width: 130px; +} +input.span1, textarea.span1, .uneditable-input.span1 { + width: 50px; +} +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + background-color: #eeeeee; + border-color: #ddd; + cursor: not-allowed; +} +.control-group.warning > label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; + border-color: #c09853; +} +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: 0 0 6px #dbc59e; + -moz-box-shadow: 0 0 6px #dbc59e; + box-shadow: 0 0 6px #dbc59e; +} +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} +.control-group.error > label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; + border-color: #b94a48; +} +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: 0 0 6px #d59392; + -moz-box-shadow: 0 0 6px #d59392; + box-shadow: 0 0 6px #d59392; +} +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} +.control-group.success > label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; + border-color: #468847; +} +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: 0 0 6px #7aba7b; + -moz-box-shadow: 0 0 6px #7aba7b; + box-shadow: 0 0 6px #7aba7b; +} +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} +input:focus:required:invalid, +textarea:focus:required:invalid, +select:focus:required:invalid { + color: #b94a48; + border-color: #ee5f5b; +} +input:focus:required:invalid:focus, +textarea:focus:required:invalid:focus, +select:focus:required:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} +.form-actions { + padding: 17px 20px 18px; + margin-top: 18px; + margin-bottom: 18px; + background-color: #eeeeee; + border-top: 1px solid #ddd; + *zoom: 1; +} +.form-actions:before, +.form-actions:after { + display: table; + content: ""; +} +.form-actions:after { + clear: both; +} +.uneditable-input { + display: block; + background-color: #ffffff; + border-color: #eee; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + cursor: not-allowed; +} +:-moz-placeholder { + color: #999999; +} +::-webkit-input-placeholder { + color: #999999; +} +.help-block, +.help-inline { + color: #555555; +} +.help-block { + display: block; + margin-bottom: 9px; +} +.help-inline { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; + vertical-align: middle; + padding-left: 5px; +} +.input-prepend, +.input-append { + margin-bottom: 5px; +} +.input-prepend input, +.input-append input, +.input-prepend select, +.input-append select, +.input-prepend .uneditable-input, +.input-append .uneditable-input { + *margin-left: 0; + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.input-prepend input:focus, +.input-append input:focus, +.input-prepend select:focus, +.input-append select:focus, +.input-prepend .uneditable-input:focus, +.input-append .uneditable-input:focus { + position: relative; + z-index: 2; +} +.input-prepend .uneditable-input, +.input-append .uneditable-input { + border-left-color: #ccc; +} +.input-prepend .add-on, +.input-append .add-on { + display: inline-block; + width: auto; + min-width: 16px; + height: 18px; + padding: 4px 5px; + font-weight: normal; + line-height: 18px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + vertical-align: middle; + background-color: #eeeeee; + border: 1px solid #ccc; +} +.input-prepend .add-on, +.input-append .add-on, +.input-prepend .btn, +.input-append .btn { + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-prepend .active, +.input-append .active { + background-color: #a9dba9; + border-color: #46a546; +} +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} +.input-append input, +.input-append select .uneditable-input { + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-append .uneditable-input { + border-left-color: #eee; + border-right-color: #ccc; +} +.input-append .add-on, +.input-append .btn { + margin-left: -1px; + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.search-query { + padding-left: 14px; + padding-right: 14px; + margin-bottom: 0; + -webkit-border-radius: 14px; + -moz-border-radius: 14px; + border-radius: 14px; +} +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + margin-bottom: 0; +} +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} +.form-search label, +.form-inline label { + display: inline-block; +} +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-left: 0; + margin-right: 3px; +} +.control-group { + margin-bottom: 9px; +} +legend + .control-group { + margin-top: 18px; + -webkit-margin-top-collapse: separate; +} +.form-horizontal .control-group { + margin-bottom: 18px; + *zoom: 1; +} +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + content: ""; +} +.form-horizontal .control-group:after { + clear: both; +} +.form-horizontal .control-label { + float: left; + width: 140px; + padding-top: 5px; + text-align: right; +} +.form-horizontal .controls { + margin-left: 160px; + /* Super jank IE7 fix to ensure the inputs in .input-append and input-prepend don't inherit the margin of the parent, in this case .controls */ + + *display: inline-block; + *margin-left: 0; + *padding-left: 20px; +} +.form-horizontal .help-block { + margin-top: 9px; + margin-bottom: 0; +} +.form-horizontal .form-actions { + padding-left: 160px; +} +.btn { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; + padding: 4px 10px 4px; + margin-bottom: 0; + font-size: 13px; + line-height: 18px; + color: #333333; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: middle; + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); + border: 1px solid #cccccc; + border-bottom-color: #b3b3b3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + cursor: pointer; + *margin-left: .3em; +} +.btn:hover, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + background-color: #e6e6e6; +} +.btn:active, +.btn.active { + background-color: #cccccc \9; +} +.btn:first-child { + *margin-left: 0; +} +.btn:hover { + color: #333333; + text-decoration: none; + background-color: #e6e6e6; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -ms-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn.active, +.btn:active { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + background-color: #e6e6e6; + background-color: #d9d9d9 \9; + outline: 0; +} +.btn.disabled, +.btn[disabled] { + cursor: default; + background-image: none; + background-color: #e6e6e6; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn-large { + padding: 9px 14px; + font-size: 15px; + line-height: normal; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.btn-large [class^="icon-"] { + margin-top: 1px; +} +.btn-small { + padding: 5px 9px; + font-size: 11px; + line-height: 16px; +} +.btn-small [class^="icon-"] { + margin-top: -1px; +} +.btn-mini { + padding: 2px 6px; + font-size: 11px; + line-height: 14px; +} +.btn-primary, +.btn-primary:hover, +.btn-warning, +.btn-warning:hover, +.btn-danger, +.btn-danger:hover, +.btn-success, +.btn-success:hover, +.btn-info, +.btn-info:hover, +.btn-inverse, +.btn-inverse:hover { + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + color: #ffffff; +} +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} +.btn-primary { + background-color: #0074cc; + background-image: -moz-linear-gradient(top, #0088cc, #0055cc); + background-image: -ms-linear-gradient(top, #0088cc, #0055cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0055cc); + background-image: -o-linear-gradient(top, #0088cc, #0055cc); + background-image: linear-gradient(top, #0088cc, #0055cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0); + border-color: #0055cc #0055cc #003580; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} +.btn-primary:hover, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + background-color: #0055cc; +} +.btn-primary:active, +.btn-primary.active { + background-color: #004099 \9; +} +.btn-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -ms-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(top, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0); + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} +.btn-warning:hover, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + background-color: #f89406; +} +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} +.btn-danger { + background-color: #da4f49; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -ms-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(top, #ee5f5b, #bd362f); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0); + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} +.btn-danger:hover, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + background-color: #bd362f; +} +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} +.btn-success { + background-color: #5bb75b; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -ms-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(top, #62c462, #51a351); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0); + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} +.btn-success:hover, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + background-color: #51a351; +} +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} +.btn-info { + background-color: #49afcd; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -ms-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(top, #5bc0de, #2f96b4); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0); + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} +.btn-info:hover, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + background-color: #2f96b4; +} +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} +.btn-inverse { + background-color: #414141; + background-image: -moz-linear-gradient(top, #555555, #222222); + background-image: -ms-linear-gradient(top, #555555, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222)); + background-image: -webkit-linear-gradient(top, #555555, #222222); + background-image: -o-linear-gradient(top, #555555, #222222); + background-image: linear-gradient(top, #555555, #222222); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0); + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); +} +.btn-inverse:hover, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + background-color: #222222; +} +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} +button.btn, +input[type="submit"].btn { + *padding-top: 2px; + *padding-bottom: 2px; +} +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + line-height: 14px; + vertical-align: text-top; + background-image: url("../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; + *margin-right: .3em; +} +[class^="icon-"]:last-child, +[class*=" icon-"]:last-child { + *margin-left: 0; +} +.icon-white { + background-image: url("../img/glyphicons-halflings-white.png"); +} +.icon-glass { + background-position: 0 0; +} +.icon-music { + background-position: -24px 0; +} +.icon-search { + background-position: -48px 0; +} +.icon-envelope { + background-position: -72px 0; +} +.icon-heart { + background-position: -96px 0; +} +.icon-star { + background-position: -120px 0; +} +.icon-star-empty { + background-position: -144px 0; +} +.icon-user { + background-position: -168px 0; +} +.icon-film { + background-position: -192px 0; +} +.icon-th-large { + background-position: -216px 0; +} +.icon-th { + background-position: -240px 0; +} +.icon-th-list { + background-position: -264px 0; +} +.icon-ok { + background-position: -288px 0; +} +.icon-remove { + background-position: -312px 0; +} +.icon-zoom-in { + background-position: -336px 0; +} +.icon-zoom-out { + background-position: -360px 0; +} +.icon-off { + background-position: -384px 0; +} +.icon-signal { + background-position: -408px 0; +} +.icon-cog { + background-position: -432px 0; +} +.icon-trash { + background-position: -456px 0; +} +.icon-home { + background-position: 0 -24px; +} +.icon-file { + background-position: -24px -24px; +} +.icon-time { + background-position: -48px -24px; +} +.icon-road { + background-position: -72px -24px; +} +.icon-download-alt { + background-position: -96px -24px; +} +.icon-download { + background-position: -120px -24px; +} +.icon-upload { + background-position: -144px -24px; +} +.icon-inbox { + background-position: -168px -24px; +} +.icon-play-circle { + background-position: -192px -24px; +} +.icon-repeat { + background-position: -216px -24px; +} +.icon-refresh { + background-position: -240px -24px; +} +.icon-list-alt { + background-position: -264px -24px; +} +.icon-lock { + background-position: -287px -24px; +} +.icon-flag { + background-position: -312px -24px; +} +.icon-headphones { + background-position: -336px -24px; +} +.icon-volume-off { + background-position: -360px -24px; +} +.icon-volume-down { + background-position: -384px -24px; +} +.icon-volume-up { + background-position: -408px -24px; +} +.icon-qrcode { + background-position: -432px -24px; +} +.icon-barcode { + background-position: -456px -24px; +} +.icon-tag { + background-position: 0 -48px; +} +.icon-tags { + background-position: -25px -48px; +} +.icon-book { + background-position: -48px -48px; +} +.icon-bookmark { + background-position: -72px -48px; +} +.icon-print { + background-position: -96px -48px; +} +.icon-camera { + background-position: -120px -48px; +} +.icon-font { + background-position: -144px -48px; +} +.icon-bold { + background-position: -167px -48px; +} +.icon-italic { + background-position: -192px -48px; +} +.icon-text-height { + background-position: -216px -48px; +} +.icon-text-width { + background-position: -240px -48px; +} +.icon-align-left { + background-position: -264px -48px; +} +.icon-align-center { + background-position: -288px -48px; +} +.icon-align-right { + background-position: -312px -48px; +} +.icon-align-justify { + background-position: -336px -48px; +} +.icon-list { + background-position: -360px -48px; +} +.icon-indent-left { + background-position: -384px -48px; +} +.icon-indent-right { + background-position: -408px -48px; +} +.icon-facetime-video { + background-position: -432px -48px; +} +.icon-picture { + background-position: -456px -48px; +} +.icon-pencil { + background-position: 0 -72px; +} +.icon-map-marker { + background-position: -24px -72px; +} +.icon-adjust { + background-position: -48px -72px; +} +.icon-tint { + background-position: -72px -72px; +} +.icon-edit { + background-position: -96px -72px; +} +.icon-share { + background-position: -120px -72px; +} +.icon-check { + background-position: -144px -72px; +} +.icon-move { + background-position: -168px -72px; +} +.icon-step-backward { + background-position: -192px -72px; +} +.icon-fast-backward { + background-position: -216px -72px; +} +.icon-backward { + background-position: -240px -72px; +} +.icon-play { + background-position: -264px -72px; +} +.icon-pause { + background-position: -288px -72px; +} +.icon-stop { + background-position: -312px -72px; +} +.icon-forward { + background-position: -336px -72px; +} +.icon-fast-forward { + background-position: -360px -72px; +} +.icon-step-forward { + background-position: -384px -72px; +} +.icon-eject { + background-position: -408px -72px; +} +.icon-chevron-left { + background-position: -432px -72px; +} +.icon-chevron-right { + background-position: -456px -72px; +} +.icon-plus-sign { + background-position: 0 -96px; +} +.icon-minus-sign { + background-position: -24px -96px; +} +.icon-remove-sign { + background-position: -48px -96px; +} +.icon-ok-sign { + background-position: -72px -96px; +} +.icon-question-sign { + background-position: -96px -96px; +} +.icon-info-sign { + background-position: -120px -96px; +} +.icon-screenshot { + background-position: -144px -96px; +} +.icon-remove-circle { + background-position: -168px -96px; +} +.icon-ok-circle { + background-position: -192px -96px; +} +.icon-ban-circle { + background-position: -216px -96px; +} +.icon-arrow-left { + background-position: -240px -96px; +} +.icon-arrow-right { + background-position: -264px -96px; +} +.icon-arrow-up { + background-position: -289px -96px; +} +.icon-arrow-down { + background-position: -312px -96px; +} +.icon-share-alt { + background-position: -336px -96px; +} +.icon-resize-full { + background-position: -360px -96px; +} +.icon-resize-small { + background-position: -384px -96px; +} +.icon-plus { + background-position: -408px -96px; +} +.icon-minus { + background-position: -433px -96px; +} +.icon-asterisk { + background-position: -456px -96px; +} +.icon-exclamation-sign { + background-position: 0 -120px; +} +.icon-gift { + background-position: -24px -120px; +} +.icon-leaf { + background-position: -48px -120px; +} +.icon-fire { + background-position: -72px -120px; +} +.icon-eye-open { + background-position: -96px -120px; +} +.icon-eye-close { + background-position: -120px -120px; +} +.icon-warning-sign { + background-position: -144px -120px; +} +.icon-plane { + background-position: -168px -120px; +} +.icon-calendar { + background-position: -192px -120px; +} +.icon-random { + background-position: -216px -120px; +} +.icon-comment { + background-position: -240px -120px; +} +.icon-magnet { + background-position: -264px -120px; +} +.icon-chevron-up { + background-position: -288px -120px; +} +.icon-chevron-down { + background-position: -313px -119px; +} +.icon-retweet { + background-position: -336px -120px; +} +.icon-shopping-cart { + background-position: -360px -120px; +} +.icon-folder-close { + background-position: -384px -120px; +} +.icon-folder-open { + background-position: -408px -120px; +} +.icon-resize-vertical { + background-position: -432px -119px; +} +.icon-resize-horizontal { + background-position: -456px -118px; +} +.btn-group { + position: relative; + *zoom: 1; + *margin-left: .3em; +} +.btn-group:before, +.btn-group:after { + display: table; + content: ""; +} +.btn-group:after { + clear: both; +} +.btn-group:first-child { + *margin-left: 0; +} +.btn-group + .btn-group { + margin-left: 5px; +} +.btn-toolbar { + margin-top: 9px; + margin-bottom: 9px; +} +.btn-toolbar .btn-group { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; +} +.btn-group .btn { + position: relative; + float: left; + margin-left: -1px; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-group .btn:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.btn-group .btn:last-child, +.btn-group .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.btn-group .btn.large:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.btn-group .btn.large:last-child, +.btn-group .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.btn-group .btn:hover, +.btn-group .btn:focus, +.btn-group .btn:active, +.btn-group .btn.active { + z-index: 2; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + *padding-top: 3px; + *padding-bottom: 3px; +} +.btn-group .btn-mini.dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 1px; + *padding-bottom: 1px; +} +.btn-group .btn-small.dropdown-toggle { + *padding-top: 4px; + *padding-bottom: 4px; +} +.btn-group .btn-large.dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} +.btn-group.open { + *z-index: 1000; +} +.btn-group.open .dropdown-menu { + display: block; + margin-top: 1px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 6px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} +.btn .caret { + margin-top: 7px; + margin-left: 0; +} +.btn:hover .caret, +.open.btn-group .caret { + opacity: 1; + filter: alpha(opacity=100); +} +.btn-mini .caret { + margin-top: 5px; +} +.btn-small .caret { + margin-top: 6px; +} +.btn-large .caret { + margin-top: 6px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 0.75; + filter: alpha(opacity=75); +} +.nav { + margin-left: 0; + margin-bottom: 18px; + list-style: none; +} +.nav > li > a { + display: block; +} +.nav > li > a:hover { + text-decoration: none; + background-color: #eeeeee; +} +.nav .nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 18px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} +.nav li + .nav-header { + margin-top: 9px; +} +.nav-list { + padding-left: 15px; + padding-right: 15px; + margin-bottom: 0; +} +.nav-list > li > a, +.nav-list .nav-header { + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} +.nav-list > li > a { + padding: 3px 15px; +} +.nav-list > .active > a, +.nav-list > .active > a:hover { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} +.nav-list [class^="icon-"] { + margin-right: 2px; +} +.nav-list .divider { + height: 1px; + margin: 8px 1px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; + *width: 100%; + *margin: -5px 0 5px; +} +.nav-tabs, +.nav-pills { + *zoom: 1; +} +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + content: ""; +} +.nav-tabs:after, +.nav-pills:after { + clear: both; +} +.nav-tabs > li, +.nav-pills > li { + float: left; +} +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + margin-bottom: -1px; +} +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 18px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover { + color: #555555; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.nav-pills > .active > a, +.nav-pills > .active > a:hover { + color: #ffffff; + background-color: #0088cc; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li > a { + margin-right: 0; +} +.nav-tabs.nav-stacked { + border-bottom: 0; +} +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.nav-tabs.nav-stacked > li > a:hover { + border-color: #ddd; + z-index: 2; +} +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} +.nav-tabs .dropdown-menu, +.nav-pills .dropdown-menu { + margin-top: 1px; + border-width: 1px; +} +.nav-pills .dropdown-menu { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.nav-tabs .dropdown-toggle .caret, +.nav-pills .dropdown-toggle .caret { + border-top-color: #0088cc; + border-bottom-color: #0088cc; + margin-top: 6px; +} +.nav-tabs .dropdown-toggle:hover .caret, +.nav-pills .dropdown-toggle:hover .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} +.nav-tabs .active .dropdown-toggle .caret, +.nav-pills .active .dropdown-toggle .caret { + border-top-color: #333333; + border-bottom-color: #333333; +} +.nav > .dropdown.active > a:hover { + color: #000000; + cursor: pointer; +} +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > .open.active > a:hover { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} +.nav .open .caret, +.nav .open.active .caret, +.nav .open a:hover .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} +.tabs-stacked .open > a:hover { + border-color: #999999; +} +.tabbable { + *zoom: 1; +} +.tabbable:before, +.tabbable:after { + display: table; + content: ""; +} +.tabbable:after { + clear: both; +} +.tab-content { + display: table; + width: 100%; +} +.tabs-below .nav-tabs, +.tabs-right .nav-tabs, +.tabs-left .nav-tabs { + border-bottom: 0; +} +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} +.tab-content > .active, +.pill-content > .active { + display: block; +} +.tabs-below .nav-tabs { + border-top: 1px solid #ddd; +} +.tabs-below .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} +.tabs-below .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.tabs-below .nav-tabs > li > a:hover { + border-bottom-color: transparent; + border-top-color: #ddd; +} +.tabs-below .nav-tabs .active > a, +.tabs-below .nav-tabs .active > a:hover { + border-color: transparent #ddd #ddd #ddd; +} +.tabs-left .nav-tabs > li, +.tabs-right .nav-tabs > li { + float: none; +} +.tabs-left .nav-tabs > li > a, +.tabs-right .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} +.tabs-left .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} +.tabs-left .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.tabs-left .nav-tabs > li > a:hover { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} +.tabs-left .nav-tabs .active > a, +.tabs-left .nav-tabs .active > a:hover { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} +.tabs-right .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} +.tabs-right .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.tabs-right .nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} +.tabs-right .nav-tabs .active > a, +.tabs-right .nav-tabs .active > a:hover { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} +.navbar { + *position: relative; + *z-index: 2; + overflow: visible; + margin-bottom: 18px; +} +.navbar-inner { + padding-left: 20px; + padding-right: 20px; + background-color: #2c2c2c; + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); +} +.navbar .container { + width: auto; +} +.btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-left: 5px; + margin-right: 5px; + background-color: #2c2c2c; + background-image: -moz-linear-gradient(top, #333333, #222222); + background-image: -ms-linear-gradient(top, #333333, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222)); + background-image: -webkit-linear-gradient(top, #333333, #222222); + background-image: -o-linear-gradient(top, #333333, #222222); + background-image: linear-gradient(top, #333333, #222222); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075); +} +.btn-navbar:hover, +.btn-navbar:active, +.btn-navbar.active, +.btn-navbar.disabled, +.btn-navbar[disabled] { + background-color: #222222; +} +.btn-navbar:active, +.btn-navbar.active { + background-color: #080808 \9; +} +.btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} +.nav-collapse.collapse { + height: auto; +} +.navbar { + color: #999999; +} +.navbar .brand:hover { + text-decoration: none; +} +.navbar .brand { + float: left; + display: block; + padding: 8px 20px 12px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + line-height: 1; + color: #ffffff; +} +.navbar .navbar-text { + margin-bottom: 0; + line-height: 40px; +} +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} +.navbar .btn-group .btn { + margin-top: 0; +} +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} +.navbar-form:before, +.navbar-form:after { + display: table; + content: ""; +} +.navbar-form:after { + clear: both; +} +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} +.navbar-form input, +.navbar-form select { + display: inline-block; + margin-bottom: 0; +} +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 6px; + white-space: nowrap; +} +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} +.navbar-search { + position: relative; + float: left; + margin-top: 6px; + margin-bottom: 0; +} +.navbar-search .search-query { + padding: 4px 9px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + color: #ffffff; + background-color: #626262; + border: 1px solid #151515; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.15); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.15); + -webkit-transition: none; + -moz-transition: none; + -ms-transition: none; + -o-transition: none; + transition: none; +} +.navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} +.navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} +.navbar-search .search-query:focus, +.navbar-search .search-query.focused { + padding: 5px 10px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + outline: 0; +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-left: 0; + padding-right: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.navbar-fixed-top { + top: 0; +} +.navbar-fixed-bottom { + bottom: 0; +} +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} +.navbar .nav.pull-right { + float: right; +} +.navbar .nav > li { + display: block; + float: left; +} +.navbar .nav > li > a { + float: none; + padding: 10px 10px 11px; + line-height: 19px; + color: #999999; + text-decoration: none; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.navbar .nav > li > a:hover { + background-color: transparent; + color: #ffffff; + text-decoration: none; +} +.navbar .nav .active > a, +.navbar .nav .active > a:hover { + color: #ffffff; + text-decoration: none; + background-color: #222222; +} +.navbar .divider-vertical { + height: 40px; + width: 1px; + margin: 0 9px; + overflow: hidden; + background-color: #222222; + border-right: 1px solid #333333; +} +.navbar .nav.pull-right { + margin-left: 10px; + margin-right: 0; +} +.navbar .dropdown-menu { + margin-top: 1px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.navbar .dropdown-menu:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 9px; +} +.navbar .dropdown-menu:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 10px; +} +.navbar-fixed-bottom .dropdown-menu:before { + border-top: 7px solid #ccc; + border-top-color: rgba(0, 0, 0, 0.2); + border-bottom: 0; + bottom: -7px; + top: auto; +} +.navbar-fixed-bottom .dropdown-menu:after { + border-top: 6px solid #ffffff; + border-bottom: 0; + bottom: -6px; + top: auto; +} +.navbar .nav .dropdown-toggle .caret, +.navbar .nav .open.dropdown .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.navbar .nav .active .caret { + opacity: 1; + filter: alpha(opacity=100); +} +.navbar .nav .open > .dropdown-toggle, +.navbar .nav .active > .dropdown-toggle, +.navbar .nav .open.active > .dropdown-toggle { + background-color: transparent; +} +.navbar .nav .active > .dropdown-toggle:hover { + color: #ffffff; +} +.navbar .nav.pull-right .dropdown-menu, +.navbar .nav .dropdown-menu.pull-right { + left: auto; + right: 0; +} +.navbar .nav.pull-right .dropdown-menu:before, +.navbar .nav .dropdown-menu.pull-right:before { + left: auto; + right: 12px; +} +.navbar .nav.pull-right .dropdown-menu:after, +.navbar .nav .dropdown-menu.pull-right:after { + left: auto; + right: 13px; +} +.breadcrumb { + padding: 7px 14px; + margin: 0 0 18px; + list-style: none; + background-color: #fbfbfb; + background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5)); + background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); + background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); + background-image: linear-gradient(top, #ffffff, #f5f5f5); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); + border: 1px solid #ddd; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} +.breadcrumb li { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; + text-shadow: 0 1px 0 #ffffff; +} +.breadcrumb .divider { + padding: 0 5px; + color: #999999; +} +.breadcrumb .active a { + color: #333333; +} +.pagination { + height: 36px; + margin: 18px 0; +} +.pagination ul { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + + *zoom: 1; + margin-left: 0; + margin-bottom: 0; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} +.pagination li { + display: inline; +} +.pagination a { + float: left; + padding: 0 14px; + line-height: 34px; + text-decoration: none; + border: 1px solid #ddd; + border-left-width: 0; +} +.pagination a:hover, +.pagination .active a { + background-color: #f5f5f5; +} +.pagination .active a { + color: #999999; + cursor: default; +} +.pagination .disabled span, +.pagination .disabled a, +.pagination .disabled a:hover { + color: #999999; + background-color: transparent; + cursor: default; +} +.pagination li:first-child a { + border-left-width: 1px; + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.pagination li:last-child a { + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.pagination-centered { + text-align: center; +} +.pagination-right { + text-align: right; +} +.pager { + margin-left: 0; + margin-bottom: 18px; + list-style: none; + text-align: center; + *zoom: 1; +} +.pager:before, +.pager:after { + display: table; + content: ""; +} +.pager:after { + clear: both; +} +.pager li { + display: inline; +} +.pager a { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + /*border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px;*/ +} +.pager a:hover { + text-decoration: none; + background-color: #f5f5f5; +} +.pager .next a { + float: right; +} +.pager .previous a { + float: left; +} +.pager .disabled a, +.pager .disabled a:hover { + color: #999999; + background-color: #fff; + cursor: default; +} +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} +.thumbnails:before, +.thumbnails:after { + display: table; + content: ""; +} +.thumbnails:after { + clear: both; +} +.thumbnails > li { + float: left; + margin: 0 0 18px 20px; +} +.thumbnail { + display: block; + padding: 4px; + line-height: 1; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); +} +a.thumbnail:hover { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} +.thumbnail > img { + display: block; + max-width: 100%; + margin-left: auto; + margin-right: auto; +} +.thumbnail .caption { + padding: 9px; +} +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 18px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + color: #c09853; +} +.alert-heading { + color: inherit; +} +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 18px; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #468847; +} +.alert-danger, +.alert-error { + background-color: #f2dede; + border-color: #eed3d7; + color: #b94a48; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #3a87ad; +} +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} +.alert-block p + p { + margin-top: 5px; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} +@-moz-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} +@-ms-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} +.progress { + overflow: hidden; + height: 18px; + margin-bottom: 18px; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -ms-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(top, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.progress .bar { + width: 0%; + height: 18px; + color: #ffffff; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -ms-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(top, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -ms-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-danger .bar { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(top, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); +} +.progress-danger.progress-striped .bar { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-success .bar { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -ms-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(top, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); +} +.progress-success.progress-striped .bar { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-info .bar { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -ms-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(top, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0); +} +.progress-info.progress-striped .bar { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-warning .bar { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -ms-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(top, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0); +} +.progress-warning.progress-striped .bar { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.hero-unit { + padding: 60px; + margin-bottom: 30px; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + color: inherit; + letter-spacing: -1px; +} +.hero-unit p { + font-size: 18px; + font-weight: 200; + line-height: 27px; + color: inherit; +} +.tooltip { + position: absolute; + z-index: 1020; + display: block; + visibility: visible; + padding: 5px; + font-size: 11px; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.tooltip.top { + margin-top: -2px; +} +.tooltip.right { + margin-left: 2px; +} +.tooltip.bottom { + margin-top: 2px; +} +.tooltip.left { + margin-left: -2px; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #000000; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + padding: 5px; +} +.popover.top { + margin-top: -5px; +} +.popover.right { + margin-left: 5px; +} +.popover.bottom { + margin-top: 5px; +} +.popover.left { + margin-left: -5px; +} +.popover.top .arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid #000000; +} +.popover.right .arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid #000000; +} +.popover.bottom .arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #000000; +} +.popover.left .arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 5px solid #000000; +} +.popover .arrow { + position: absolute; + width: 0; + height: 0; +} +.popover-inner { + padding: 3px; + width: 280px; + overflow: hidden; + background: #000000; + background: rgba(0, 0, 0, 0.8); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); +} +.popover-title { + padding: 9px 15px; + line-height: 1; + background-color: #f5f5f5; + border-bottom: 1px solid #eee; + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; +} +.popover-content { + padding: 14px; + background-color: #ffffff; + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.popover-content p, +.popover-content ul, +.popover-content ol { + margin-bottom: 0; +} +.modal-open .dropdown-menu { + z-index: 2050; +} +.modal-open .dropdown.open { + *z-index: 2050; +} +.modal-open .popover { + z-index: 2060; +} +.modal-open .tooltip { + z-index: 2070; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} +.modal-backdrop.fade { + opacity: 0; +} +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.modal { + position: fixed; + top: 50%; + left: 50%; + z-index: 1050; + overflow: auto; + width: 560px; + margin: -250px 0 0 -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + /* IE6-7 */ + + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.modal.fade { + -webkit-transition: opacity .3s linear, top .3s ease-out; + -moz-transition: opacity .3s linear, top .3s ease-out; + -ms-transition: opacity .3s linear, top .3s ease-out; + -o-transition: opacity .3s linear, top .3s ease-out; + transition: opacity .3s linear, top .3s ease-out; + top: -25%; +} +.modal.fade.in { + top: 50%; +} +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} +.modal-header .close { + margin-top: 2px; +} +.modal-body { + overflow-y: auto; + max-height: 400px; + padding: 15px; +} +.modal-form { + margin-bottom: 0; +} +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; + *zoom: 1; +} +.modal-footer:before, +.modal-footer:after { + display: table; + content: ""; +} +.modal-footer:after { + clear: both; +} +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.dropdown { + position: relative; +} +.dropdown-toggle { + *margin-bottom: -3px; +} +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #000000; + opacity: 0.3; + filter: alpha(opacity=30); + content: ""; +} +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} +.dropdown:hover .caret, +.open.dropdown .caret { + opacity: 1; + filter: alpha(opacity=100); +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + float: left; + display: none; + min-width: 160px; + padding: 4px 0; + margin: 0; + list-style: none; + background-color: #ffffff; + border-color: #ccc; + border-color: rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 1px; + -webkit-border-radius: 0 0 5px 5px; + -moz-border-radius: 0 0 5px 5px; + border-radius: 0 0 5px 5px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + *border-right-width: 2px; + *border-bottom-width: 2px; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 8px 1px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; + *width: 100%; + *margin: -5px 0 5px; +} +.dropdown-menu a { + display: block; + padding: 3px 15px; + clear: both; + font-weight: normal; + line-height: 18px; + color: #333333; + white-space: nowrap; +} +.dropdown-menu li > a:hover, +.dropdown-menu .active > a, +.dropdown-menu .active > a:hover { + color: #ffffff; + text-decoration: none; + background-color: #0088cc; +} +.dropdown.open { + *z-index: 1000; +} +.dropdown.open .dropdown-toggle { + color: #ffffff; + background: #ccc; + background: rgba(0, 0, 0, 0.3); +} +.dropdown.open .dropdown-menu { + display: block; +} +.pull-right .dropdown-menu { + left: auto; + right: 0; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: "\2191"; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +.typeahead { + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.accordion { + margin-bottom: 18px; +} +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.accordion-heading { + border-bottom: 0; +} +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} +.carousel { + position: relative; + margin-bottom: 18px; + line-height: 1; +} +.carousel-inner { + overflow: hidden; + width: 100%; + position: relative; +} +.carousel .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -ms-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel .item > img { + display: block; + line-height: 1; +} +.carousel .active, +.carousel .next, +.carousel .prev { + display: block; +} +.carousel .active { + left: 0; +} +.carousel .next, +.carousel .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel .next { + left: 100%; +} +.carousel .prev { + left: -100%; +} +.carousel .next.left, +.carousel .prev.right { + left: 0; +} +.carousel .active.left { + left: -100%; +} +.carousel .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} +.carousel-control.right { + left: auto; + right: 15px; +} +.carousel-control:hover { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-caption { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 10px 15px 5px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} +.carousel-caption h4, +.carousel-caption p { + color: #ffffff; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #eee; + border: 1px solid rgba(0, 0, 0, 0.05); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 18px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover { + color: #000000; + text-decoration: none; + opacity: 0.4; + filter: alpha(opacity=40); + cursor: pointer; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.hide { + display: none; +} +.show { + display: block; +} +.invisible { + visibility: hidden; +} +.fade { + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -ms-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; + opacity: 0; +} +.fade.in { + opacity: 1; +} +.collapse { + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -ms-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; + position: relative; + overflow: hidden; + height: 0; +} +.collapse.in { + height: auto; +} +/*! + * Bootstrap Responsive v2.0.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +.hidden { + display: none; + visibility: hidden; +} +.visible-phone { + display: none; +} +.visible-tablet { + display: none; +} +.visible-desktop { + display: block; +} +.hidden-phone { + display: block; +} +.hidden-tablet { + display: block; +} +.hidden-desktop { + display: none; +} +@media (max-width: 767px) { + .visible-phone { + display: block; + } + .hidden-phone { + display: none; + } + .hidden-desktop { + display: block; + } + .visible-desktop { + display: none; + } +} +@media (min-width: 768px) and (max-width: 979px) { + .visible-tablet { + display: block; + } + .hidden-tablet { + display: none; + } + .hidden-desktop { + display: block; + } + .visible-desktop { + display: none; + } +} +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 18px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-group > label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-left: 10px; + padding-right: 10px; + } + .modal { + position: absolute; + top: 10px; + left: 10px; + right: 10px; + width: auto; + margin: 0; + } + .modal.fade.in { + top: auto; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} +@media (max-width: 767px) { + body { + padding-left: 20px; + padding-right: 20px; + } + .navbar-fixed-top { + margin-left: -20px; + margin-right: -20px; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row { + margin-left: 0; + } + .row > [class*="span"], + .row-fluid > [class*="span"] { + float: none; + display: block; + width: auto; + margin: 0; + } + .thumbnails [class*="span"] { + width: auto; + } + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 28px; + /* Make inputs at least the height of their button counterpart */ + + /* Makes inputs behave like true block-level elements */ + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + width: auto; + } +} +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + margin-left: 20px; + } + .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid > [class*="span"] { + float: left; + margin-left: 2.762430939%; + } + .row-fluid > [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid > .span12 { + width: 99.999999993%; + } + .row-fluid > .span11 { + width: 91.436464082%; + } + .row-fluid > .span10 { + width: 82.87292817100001%; + } + .row-fluid > .span9 { + width: 74.30939226%; + } + .row-fluid > .span8 { + width: 65.74585634900001%; + } + .row-fluid > .span7 { + width: 57.182320438000005%; + } + .row-fluid > .span6 { + width: 48.618784527%; + } + .row-fluid > .span5 { + width: 40.055248616%; + } + .row-fluid > .span4 { + width: 31.491712705%; + } + .row-fluid > .span3 { + width: 22.928176794%; + } + .row-fluid > .span2 { + width: 14.364640883%; + } + .row-fluid > .span1 { + width: 5.801104972%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + input.span12, textarea.span12, .uneditable-input.span12 { + width: 714px; + } + input.span11, textarea.span11, .uneditable-input.span11 { + width: 652px; + } + input.span10, textarea.span10, .uneditable-input.span10 { + width: 590px; + } + input.span9, textarea.span9, .uneditable-input.span9 { + width: 528px; + } + input.span8, textarea.span8, .uneditable-input.span8 { + width: 466px; + } + input.span7, textarea.span7, .uneditable-input.span7 { + width: 404px; + } + input.span6, textarea.span6, .uneditable-input.span6 { + width: 342px; + } + input.span5, textarea.span5, .uneditable-input.span5 { + width: 280px; + } + input.span4, textarea.span4, .uneditable-input.span4 { + width: 218px; + } + input.span3, textarea.span3, .uneditable-input.span3 { + width: 156px; + } + input.span2, textarea.span2, .uneditable-input.span2 { + width: 94px; + } + input.span1, textarea.span1, .uneditable-input.span1 { + width: 32px; + } +} +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top { + position: static; + margin-bottom: 18px; + } + .navbar-fixed-top .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-left: 10px; + padding-right: 10px; + margin: 0 0 0 -5px; + } + .navbar .nav-collapse { + clear: left; + } + .navbar .nav { + float: none; + margin: 0 0 9px; + } + .navbar .nav > li { + float: none; + } + .navbar .nav > li > a { + margin-bottom: 2px; + } + .navbar .nav > .divider-vertical { + display: none; + } + .navbar .nav .nav-header { + color: #999999; + text-shadow: none; + } + .navbar .nav > li > a, + .navbar .dropdown-menu a { + padding: 6px 15px; + font-weight: bold; + color: #999999; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .navbar .dropdown-menu li + li a { + margin-bottom: 2px; + } + .navbar .nav > li > a:hover, + .navbar .dropdown-menu a:hover { + background-color: #222222; + } + .navbar .dropdown-menu { + position: static; + top: auto; + left: auto; + float: none; + display: block; + max-width: none; + margin: 0 15px; + padding: 0; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .navbar .dropdown-menu:before, + .navbar .dropdown-menu:after { + display: none; + } + .navbar .dropdown-menu .divider { + display: none; + } + .navbar-form, + .navbar-search { + float: none; + padding: 9px 15px; + margin: 9px 0; + border-top: 1px solid #222222; + border-bottom: 1px solid #222222; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + } + .navbar .nav.pull-right { + float: none; + margin-left: 0; + } + .navbar-static .navbar-inner { + padding-left: 10px; + padding-right: 10px; + } + .btn-navbar { + display: block; + } + .nav-collapse { + overflow: hidden; + height: 0; + } +} +@media (min-width: 980px) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + content: ""; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + margin-left: 30px; + } + .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + content: ""; + } + .row-fluid:after { + clear: both; + } + .row-fluid > [class*="span"] { + float: left; + margin-left: 2.564102564%; + } + .row-fluid > [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid > .span12 { + width: 100%; + } + .row-fluid > .span11 { + width: 91.45299145300001%; + } + .row-fluid > .span10 { + width: 82.905982906%; + } + .row-fluid > .span9 { + width: 74.358974359%; + } + .row-fluid > .span8 { + width: 65.81196581200001%; + } + .row-fluid > .span7 { + width: 57.264957265%; + } + .row-fluid > .span6 { + width: 48.717948718%; + } + .row-fluid > .span5 { + width: 40.170940171000005%; + } + .row-fluid > .span4 { + width: 31.623931624%; + } + .row-fluid > .span3 { + width: 23.076923077%; + } + .row-fluid > .span2 { + width: 14.529914530000001%; + } + .row-fluid > .span1 { + width: 5.982905983%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + input.span12, textarea.span12, .uneditable-input.span12 { + width: 1160px; + } + input.span11, textarea.span11, .uneditable-input.span11 { + width: 1060px; + } + input.span10, textarea.span10, .uneditable-input.span10 { + width: 960px; + } + input.span9, textarea.span9, .uneditable-input.span9 { + width: 860px; + } + input.span8, textarea.span8, .uneditable-input.span8 { + width: 760px; + } + input.span7, textarea.span7, .uneditable-input.span7 { + width: 660px; + } + input.span6, textarea.span6, .uneditable-input.span6 { + width: 560px; + } + input.span5, textarea.span5, .uneditable-input.span5 { + width: 460px; + } + input.span4, textarea.span4, .uneditable-input.span4 { + width: 360px; + } + input.span3, textarea.span3, .uneditable-input.span3 { + width: 260px; + } + input.span2, textarea.span2, .uneditable-input.span2 { + width: 160px; + } + input.span1, textarea.span1, .uneditable-input.span1 { + width: 60px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } +} diff --git a/askbot/skins/default/media/bootstrap/css/bootstrap.min.css b/askbot/skins/default/media/bootstrap/css/bootstrap.min.css new file mode 100644 index 00000000..17b18fb6 --- /dev/null +++ b/askbot/skins/default/media/bootstrap/css/bootstrap.min.css @@ -0,0 +1,706 @@ +/*! + * Bootstrap v2.0.2 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} +.clearfix:after{clear:both;} +.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;} +.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} +audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} +audio:not([controls]){display:none;} +html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} +a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +a:hover,a:active{outline:0;} +sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;} +sup{top:-0.5em;} +sub{bottom:-0.25em;} +img{height:auto;border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;} +button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;} +button,input{*overflow:visible;line-height:normal;} +button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;} +button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} +input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} +input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;} +textarea{overflow:auto;vertical-align:top;} +body{margin:0;font-family:Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;} +a{color:#0088cc;text-decoration:none;} +a:hover{color:#005580;text-decoration:underline;} +.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} +.row:after{clear:both;} +[class*="span"]{float:left;margin-left:20px;} +.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;} +.span12{width:940px;} +.span11{width:860px;} +.span10{width:780px;} +.span9{width:700px;} +.span8{width:620px;} +.span7{width:540px;} +.span6{width:460px;} +.span5{width:380px;} +.span4{width:300px;} +.span3{width:220px;} +.span2{width:140px;} +.span1{width:60px;} +.offset12{margin-left:980px;} +.offset11{margin-left:900px;} +.offset10{margin-left:820px;} +.offset9{margin-left:740px;} +.offset8{margin-left:660px;} +.offset7{margin-left:580px;} +.offset6{margin-left:500px;} +.offset5{margin-left:420px;} +.offset4{margin-left:340px;} +.offset3{margin-left:260px;} +.offset2{margin-left:180px;} +.offset1{margin-left:100px;} +.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} +.row-fluid:after{clear:both;} +.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;} +.row-fluid>[class*="span"]:first-child{margin-left:0;} +.row-fluid > .span12{width:99.99999998999999%;} +.row-fluid > .span11{width:91.489361693%;} +.row-fluid > .span10{width:82.97872339599999%;} +.row-fluid > .span9{width:74.468085099%;} +.row-fluid > .span8{width:65.95744680199999%;} +.row-fluid > .span7{width:57.446808505%;} +.row-fluid > .span6{width:48.93617020799999%;} +.row-fluid > .span5{width:40.425531911%;} +.row-fluid > .span4{width:31.914893614%;} +.row-fluid > .span3{width:23.404255317%;} +.row-fluid > .span2{width:14.89361702%;} +.row-fluid > .span1{width:6.382978723%;} +.container{margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";} +.container:after{clear:both;} +.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";} +.container-fluid:after{clear:both;} +p{margin:0 0 9px;font-family:Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;} +.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;} +h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;} +h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;} +h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;} +h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;} +h4,h5,h6{line-height:18px;} +h4{font-size:14px;}h4 small{font-size:12px;} +h5{font-size:12px;} +h6{font-size:11px;color:#999999;text-transform:uppercase;} +.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;} +.page-header h1{line-height:1;} +ul,ol{padding:0;margin:0 0 9px 25px;} +ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} +ul{list-style:disc;} +ol{list-style:decimal;} +li{line-height:18px;} +ul.unstyled,ol.unstyled{margin-left:0;list-style:none;} +dl{margin-bottom:18px;} +dt,dd{line-height:18px;} +dt{font-weight:bold;line-height:17px;} +dd{margin-left:9px;} +.dl-horizontal dt{float:left;clear:left;width:120px;text-align:right;} +.dl-horizontal dd{margin-left:130px;} +hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;} +strong{font-weight:bold;} +em{font-style:italic;} +.muted{color:#999999;} +abbr[title]{border-bottom:1px dotted #ddd;cursor:help;} +abbr.initialism{font-size:90%;text-transform:uppercase;} +blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;} +blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';} +blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;} +q:before,q:after,blockquote:before,blockquote:after{content:"";} +address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;} +small{font-size:100%;} +cite{font-style:normal;} +code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} +pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;}pre.prettyprint{margin-bottom:18px;} +pre code{padding:0;color:inherit;background-color:transparent;border:0;} +.pre-scrollable{max-height:340px;overflow-y:scroll;} +.label{padding:1px 4px 2px;font-size:10.998px;font-weight:bold;line-height:13px;color:#ffffff;vertical-align:middle;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.label:hover{color:#ffffff;text-decoration:none;} +.label-important{background-color:#b94a48;} +.label-important:hover{background-color:#953b39;} +.label-warning{background-color:#f89406;} +.label-warning:hover{background-color:#c67605;} +.label-success{background-color:#468847;} +.label-success:hover{background-color:#356635;} +.label-info{background-color:#3a87ad;} +.label-info:hover{background-color:#2d6987;} +.label-inverse{background-color:#333333;} +.label-inverse:hover{background-color:#1a1a1a;} +.badge{padding:1px 9px 2px;font-size:12.025px;font-weight:bold;white-space:nowrap;color:#ffffff;background-color:#999999;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;} +.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;} +.badge-error{background-color:#b94a48;} +.badge-error:hover{background-color:#953b39;} +.badge-warning{background-color:#f89406;} +.badge-warning:hover{background-color:#c67605;} +.badge-success{background-color:#468847;} +.badge-success:hover{background-color:#356635;} +.badge-info{background-color:#3a87ad;} +.badge-info:hover{background-color:#2d6987;} +.badge-inverse{background-color:#333333;} +.badge-inverse:hover{background-color:#1a1a1a;} +table{max-width:100%;border-collapse:collapse;border-spacing:0;background-color:transparent;} +.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;} +.table th{font-weight:bold;} +.table thead th{vertical-align:bottom;} +.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;} +.table tbody+tbody{border-top:2px solid #dddddd;} +.table-condensed th,.table-condensed td{padding:4px 5px;} +.table-bordered{border:1px solid #dddddd;border-left:0;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;} +.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;} +.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} +.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} +.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} +.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} +.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} +.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;} +table .span1{float:none;width:44px;margin-left:0;} +table .span2{float:none;width:124px;margin-left:0;} +table .span3{float:none;width:204px;margin-left:0;} +table .span4{float:none;width:284px;margin-left:0;} +table .span5{float:none;width:364px;margin-left:0;} +table .span6{float:none;width:444px;margin-left:0;} +table .span7{float:none;width:524px;margin-left:0;} +table .span8{float:none;width:604px;margin-left:0;} +table .span9{float:none;width:684px;margin-left:0;} +table .span10{float:none;width:764px;margin-left:0;} +table .span11{float:none;width:844px;margin-left:0;} +table .span12{float:none;width:924px;margin-left:0;} +table .span13{float:none;width:1004px;margin-left:0;} +table .span14{float:none;width:1084px;margin-left:0;} +table .span15{float:none;width:1164px;margin-left:0;} +table .span16{float:none;width:1244px;margin-left:0;} +table .span17{float:none;width:1324px;margin-left:0;} +table .span18{float:none;width:1404px;margin-left:0;} +table .span19{float:none;width:1484px;margin-left:0;} +table .span20{float:none;width:1564px;margin-left:0;} +table .span21{float:none;width:1644px;margin-left:0;} +table .span22{float:none;width:1724px;margin-left:0;} +table .span23{float:none;width:1804px;margin-left:0;} +table .span24{float:none;width:1884px;margin-left:0;} +form{margin:0 0 18px;} +fieldset{padding:0;margin:0;border:0;} +legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;} +label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;} +input,button,select,textarea{font-family:Arial,sans-serif;} +label{display:block;margin-bottom:5px;color:#333333;} +input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.uneditable-textarea{width:auto;height:auto;} +label input,label textarea,label select{display:block;} +input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:0 \9;} +input[type="image"]{border:0;} +input[type="file"]{width:auto;padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;} +select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;} +input[type="file"]{line-height:18px \9;} +select{width:220px;background-color:#ffffff;} +select[multiple],select[size]{height:auto;} +input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +textarea{height:auto;} +input[type="hidden"]{display:none;} +.radio,.checkbox{padding-left:18px;} +.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;} +.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;} +.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;} +.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;} +input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;} +input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;} +input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +.input-mini{width:60px;} +.input-small{width:90px;} +.input-medium{width:150px;} +.input-large{width:210px;} +.input-xlarge{width:270px;} +.input-xxlarge{width:530px;} +input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;} +input,textarea,.uneditable-input{margin-left:0;} +input.span12, textarea.span12, .uneditable-input.span12{width:930px;} +input.span11, textarea.span11, .uneditable-input.span11{width:850px;} +input.span10, textarea.span10, .uneditable-input.span10{width:770px;} +input.span9, textarea.span9, .uneditable-input.span9{width:690px;} +input.span8, textarea.span8, .uneditable-input.span8{width:610px;} +input.span7, textarea.span7, .uneditable-input.span7{width:530px;} +input.span6, textarea.span6, .uneditable-input.span6{width:450px;} +input.span5, textarea.span5, .uneditable-input.span5{width:370px;} +input.span4, textarea.span4, .uneditable-input.span4{width:290px;} +input.span3, textarea.span3, .uneditable-input.span3{width:210px;} +input.span2, textarea.span2, .uneditable-input.span2{width:130px;} +input.span1, textarea.span1, .uneditable-input.span1{width:50px;} +input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#eeeeee;border-color:#ddd;cursor:not-allowed;} +.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;} +.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;} +.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;} +.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;} +.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;} +.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;} +.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;} +.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;} +.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;} +input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;} +.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#eeeeee;border-top:1px solid #ddd;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";} +.form-actions:after{clear:both;} +.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;} +:-moz-placeholder{color:#999999;} +::-webkit-input-placeholder{color:#999999;} +.help-block,.help-inline{color:#555555;} +.help-block{display:block;margin-bottom:9px;} +.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;} +.input-prepend,.input-append{margin-bottom:5px;}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{*margin-left:0;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;} +.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;} +.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;min-width:16px;height:18px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #ffffff;vertical-align:middle;background-color:#eeeeee;border:1px solid #ccc;} +.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;} +.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;} +.input-append input,.input-append select .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append .uneditable-input{border-left-color:#eee;border-right-color:#ccc;} +.input-append .add-on,.input-append .btn{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;} +.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;margin-bottom:0;} +.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;} +.form-search label,.form-inline label{display:inline-block;} +.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;} +.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;} +.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-left:0;margin-right:3px;} +.control-group{margin-bottom:9px;} +legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;} +.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";} +.form-horizontal .control-group:after{clear:both;} +.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;} +.form-horizontal .controls{margin-left:160px;*display:inline-block;*margin-left:0;*padding-left:20px;} +.form-horizontal .help-block{margin-top:9px;margin-bottom:0;} +.form-horizontal .form-actions{padding-left:160px;} +.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 10px 4px;margin-bottom:0;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #cccccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;} +.btn:active,.btn.active{background-color:#cccccc \9;} +.btn:first-child{*margin-left:0;} +.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;} +.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;outline:0;} +.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.btn-large [class^="icon-"]{margin-top:1px;} +.btn-small{padding:5px 9px;font-size:11px;line-height:16px;} +.btn-small [class^="icon-"]{margin-top:-1px;} +.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;} +.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;} +.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);} +.btn-primary{background-color:#0074cc;background-image:-moz-linear-gradient(top, #0088cc, #0055cc);background-image:-ms-linear-gradient(top, #0088cc, #0055cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));background-image:-webkit-linear-gradient(top, #0088cc, #0055cc);background-image:-o-linear-gradient(top, #0088cc, #0055cc);background-image:linear-gradient(top, #0088cc, #0055cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);border-color:#0055cc #0055cc #003580;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0055cc;} +.btn-primary:active,.btn-primary.active{background-color:#004099 \9;} +.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;} +.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;} +.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;} +.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;} +.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;} +.btn-success:active,.btn-success.active{background-color:#408140 \9;} +.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;} +.btn-info:active,.btn-info.active{background-color:#24748c \9;} +.btn-inverse{background-color:#414141;background-image:-moz-linear-gradient(top, #555555, #222222);background-image:-ms-linear-gradient(top, #555555, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));background-image:-webkit-linear-gradient(top, #555555, #222222);background-image:-o-linear-gradient(top, #555555, #222222);background-image:linear-gradient(top, #555555, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222222;} +.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;} +button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;} +button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;} +button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;} +button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;} +[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;} +.icon-white{background-image:url("../img/glyphicons-halflings-white.png");} +.icon-glass{background-position:0 0;} +.icon-music{background-position:-24px 0;} +.icon-search{background-position:-48px 0;} +.icon-envelope{background-position:-72px 0;} +.icon-heart{background-position:-96px 0;} +.icon-star{background-position:-120px 0;} +.icon-star-empty{background-position:-144px 0;} +.icon-user{background-position:-168px 0;} +.icon-film{background-position:-192px 0;} +.icon-th-large{background-position:-216px 0;} +.icon-th{background-position:-240px 0;} +.icon-th-list{background-position:-264px 0;} +.icon-ok{background-position:-288px 0;} +.icon-remove{background-position:-312px 0;} +.icon-zoom-in{background-position:-336px 0;} +.icon-zoom-out{background-position:-360px 0;} +.icon-off{background-position:-384px 0;} +.icon-signal{background-position:-408px 0;} +.icon-cog{background-position:-432px 0;} +.icon-trash{background-position:-456px 0;} +.icon-home{background-position:0 -24px;} +.icon-file{background-position:-24px -24px;} +.icon-time{background-position:-48px -24px;} +.icon-road{background-position:-72px -24px;} +.icon-download-alt{background-position:-96px -24px;} +.icon-download{background-position:-120px -24px;} +.icon-upload{background-position:-144px -24px;} +.icon-inbox{background-position:-168px -24px;} +.icon-play-circle{background-position:-192px -24px;} +.icon-repeat{background-position:-216px -24px;} +.icon-refresh{background-position:-240px -24px;} +.icon-list-alt{background-position:-264px -24px;} +.icon-lock{background-position:-287px -24px;} +.icon-flag{background-position:-312px -24px;} +.icon-headphones{background-position:-336px -24px;} +.icon-volume-off{background-position:-360px -24px;} +.icon-volume-down{background-position:-384px -24px;} +.icon-volume-up{background-position:-408px -24px;} +.icon-qrcode{background-position:-432px -24px;} +.icon-barcode{background-position:-456px -24px;} +.icon-tag{background-position:0 -48px;} +.icon-tags{background-position:-25px -48px;} +.icon-book{background-position:-48px -48px;} +.icon-bookmark{background-position:-72px -48px;} +.icon-print{background-position:-96px -48px;} +.icon-camera{background-position:-120px -48px;} +.icon-font{background-position:-144px -48px;} +.icon-bold{background-position:-167px -48px;} +.icon-italic{background-position:-192px -48px;} +.icon-text-height{background-position:-216px -48px;} +.icon-text-width{background-position:-240px -48px;} +.icon-align-left{background-position:-264px -48px;} +.icon-align-center{background-position:-288px -48px;} +.icon-align-right{background-position:-312px -48px;} +.icon-align-justify{background-position:-336px -48px;} +.icon-list{background-position:-360px -48px;} +.icon-indent-left{background-position:-384px -48px;} +.icon-indent-right{background-position:-408px -48px;} +.icon-facetime-video{background-position:-432px -48px;} +.icon-picture{background-position:-456px -48px;} +.icon-pencil{background-position:0 -72px;} +.icon-map-marker{background-position:-24px -72px;} +.icon-adjust{background-position:-48px -72px;} +.icon-tint{background-position:-72px -72px;} +.icon-edit{background-position:-96px -72px;} +.icon-share{background-position:-120px -72px;} +.icon-check{background-position:-144px -72px;} +.icon-move{background-position:-168px -72px;} +.icon-step-backward{background-position:-192px -72px;} +.icon-fast-backward{background-position:-216px -72px;} +.icon-backward{background-position:-240px -72px;} +.icon-play{background-position:-264px -72px;} +.icon-pause{background-position:-288px -72px;} +.icon-stop{background-position:-312px -72px;} +.icon-forward{background-position:-336px -72px;} +.icon-fast-forward{background-position:-360px -72px;} +.icon-step-forward{background-position:-384px -72px;} +.icon-eject{background-position:-408px -72px;} +.icon-chevron-left{background-position:-432px -72px;} +.icon-chevron-right{background-position:-456px -72px;} +.icon-plus-sign{background-position:0 -96px;} +.icon-minus-sign{background-position:-24px -96px;} +.icon-remove-sign{background-position:-48px -96px;} +.icon-ok-sign{background-position:-72px -96px;} +.icon-question-sign{background-position:-96px -96px;} +.icon-info-sign{background-position:-120px -96px;} +.icon-screenshot{background-position:-144px -96px;} +.icon-remove-circle{background-position:-168px -96px;} +.icon-ok-circle{background-position:-192px -96px;} +.icon-ban-circle{background-position:-216px -96px;} +.icon-arrow-left{background-position:-240px -96px;} +.icon-arrow-right{background-position:-264px -96px;} +.icon-arrow-up{background-position:-289px -96px;} +.icon-arrow-down{background-position:-312px -96px;} +.icon-share-alt{background-position:-336px -96px;} +.icon-resize-full{background-position:-360px -96px;} +.icon-resize-small{background-position:-384px -96px;} +.icon-plus{background-position:-408px -96px;} +.icon-minus{background-position:-433px -96px;} +.icon-asterisk{background-position:-456px -96px;} +.icon-exclamation-sign{background-position:0 -120px;} +.icon-gift{background-position:-24px -120px;} +.icon-leaf{background-position:-48px -120px;} +.icon-fire{background-position:-72px -120px;} +.icon-eye-open{background-position:-96px -120px;} +.icon-eye-close{background-position:-120px -120px;} +.icon-warning-sign{background-position:-144px -120px;} +.icon-plane{background-position:-168px -120px;} +.icon-calendar{background-position:-192px -120px;} +.icon-random{background-position:-216px -120px;} +.icon-comment{background-position:-240px -120px;} +.icon-magnet{background-position:-264px -120px;} +.icon-chevron-up{background-position:-288px -120px;} +.icon-chevron-down{background-position:-313px -119px;} +.icon-retweet{background-position:-336px -120px;} +.icon-shopping-cart{background-position:-360px -120px;} +.icon-folder-close{background-position:-384px -120px;} +.icon-folder-open{background-position:-408px -120px;} +.icon-resize-vertical{background-position:-432px -119px;} +.icon-resize-horizontal{background-position:-456px -118px;} +.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";} +.btn-group:after{clear:both;} +.btn-group:first-child{*margin-left:0;} +.btn-group+.btn-group{margin-left:5px;} +.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;} +.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;} +.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;} +.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;} +.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;} +.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;} +.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;} +.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:3px;*padding-bottom:3px;} +.btn-group .btn-mini.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:1px;*padding-bottom:1px;} +.btn-group .btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px;} +.btn-group .btn-large.dropdown-toggle{padding-left:12px;padding-right:12px;} +.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);} +.btn .caret{margin-top:7px;margin-left:0;} +.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);} +.btn-mini .caret{margin-top:5px;} +.btn-small .caret{margin-top:6px;} +.btn-large .caret{margin-top:6px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);} +.nav{margin-left:0;margin-bottom:18px;list-style:none;} +.nav>li>a{display:block;} +.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;} +.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;} +.nav li+.nav-header{margin-top:9px;} +.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;} +.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} +.nav-list>li>a{padding:3px 15px;} +.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;} +.nav-list [class^="icon-"]{margin-right:2px;} +.nav-list .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;} +.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";} +.nav-tabs:after,.nav-pills:after{clear:both;} +.nav-tabs>li,.nav-pills>li{float:left;} +.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;} +.nav-tabs{border-bottom:1px solid #ddd;} +.nav-tabs>li{margin-bottom:-1px;} +.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;} +.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} +.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;} +.nav-stacked>li{float:none;} +.nav-stacked>li>a{margin-right:0;} +.nav-tabs.nav-stacked{border-bottom:0;} +.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;} +.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;} +.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;} +.nav-pills.nav-stacked>li>a{margin-bottom:3px;} +.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;} +.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;} +.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;} +.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;} +.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;} +.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;} +.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;} +.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);} +.tabs-stacked .open>a:hover{border-color:#999999;} +.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";} +.tabbable:after{clear:both;} +.tab-content{display:table;width:100%;} +.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;} +.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} +.tab-content>.active,.pill-content>.active{display:block;} +.tabs-below .nav-tabs{border-top:1px solid #ddd;} +.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;} +.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;} +.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;} +.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;} +.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;} +.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;} +.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;} +.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;} +.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;} +.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;} +.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;} +.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;} +.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;} +.navbar{*position:relative;*z-index:2;overflow:visible;margin-bottom:18px;} +.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} +.navbar .container{width:auto;} +.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;} +.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;} +.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);} +.btn-navbar .icon-bar+.icon-bar{margin-top:3px;} +.nav-collapse.collapse{height:auto;} +.navbar{color:#999999;}.navbar .brand:hover{text-decoration:none;} +.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;} +.navbar .navbar-text{margin-bottom:0;line-height:40px;} +.navbar .btn,.navbar .btn-group{margin-top:5px;} +.navbar .btn-group .btn{margin-top:0;} +.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";} +.navbar-form:after{clear:both;} +.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;} +.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;} +.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;} +.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;} +.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query:-moz-placeholder{color:#cccccc;} +.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;} +.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;} +.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;} +.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;} +.navbar-fixed-top{top:0;} +.navbar-fixed-bottom{bottom:0;} +.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;} +.navbar .nav.pull-right{float:right;} +.navbar .nav>li{display:block;float:left;} +.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} +.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;} +.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;} +.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;} +.navbar .nav.pull-right{margin-left:10px;margin-right:0;} +.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;} +.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;} +.navbar-fixed-bottom .dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;} +.navbar-fixed-bottom .dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;} +.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;} +.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);} +.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;} +.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;} +.navbar .nav.pull-right .dropdown-menu,.navbar .nav .dropdown-menu.pull-right{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before,.navbar .nav .dropdown-menu.pull-right:before{left:auto;right:12px;} +.navbar .nav.pull-right .dropdown-menu:after,.navbar .nav .dropdown-menu.pull-right:after{left:auto;right:13px;} +.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;} +.breadcrumb .divider{padding:0 5px;color:#999999;} +.breadcrumb .active a{color:#333333;} +.pagination{height:36px;margin:18px 0;} +.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);} +.pagination li{display:inline;} +.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;} +.pagination a:hover,.pagination .active a{background-color:#f5f5f5;} +.pagination .active a{color:#999999;cursor:default;} +.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;} +.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.pagination-centered{text-align:center;} +.pagination-right{text-align:right;} +.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";} +.pager:after{clear:both;} +.pager li{display:inline;} +.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;} +.pager a:hover{text-decoration:none;background-color:#f5f5f5;} +.pager .next a{float:right;} +.pager .previous a{float:left;} +.pager .disabled a,.pager .disabled a:hover{color:#999999;background-color:#fff;cursor:default;} +.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";} +.thumbnails:after{clear:both;} +.thumbnails>li{float:left;margin:0 0 18px 20px;} +.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);} +a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);} +.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;} +.thumbnail .caption{padding:9px;} +.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;} +.alert-heading{color:inherit;} +.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;} +.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;} +.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;} +.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;} +.alert-block{padding-top:14px;padding-bottom:14px;} +.alert-block>p,.alert-block>ul{margin-bottom:0;} +.alert-block p+p{margin-top:5px;} +@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;} +.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;} +.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;} +.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);} +.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);} +.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);} +.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);} +.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;} +.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;} +.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);} +.tooltip.top{margin-top:-2px;} +.tooltip.right{margin-left:2px;} +.tooltip.bottom{margin-top:2px;} +.tooltip.left{margin-left:-2px;} +.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.tooltip-arrow{position:absolute;width:0;height:0;} +.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;} +.popover.right{margin-left:5px;} +.popover.bottom{margin-top:5px;} +.popover.left{margin-left:-5px;} +.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.popover .arrow{position:absolute;width:0;height:0;} +.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);} +.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;} +.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;} +.modal-open .dropdown-menu{z-index:2050;} +.modal-open .dropdown.open{*z-index:2050;} +.modal-open .popover{z-index:2060;} +.modal-open .tooltip{z-index:2070;} +.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;} +.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);} +.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;} +.modal.fade.in{top:50%;} +.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;} +.modal-body{overflow-y:auto;max-height:400px;padding:15px;} +.modal-form{margin-bottom:0;} +.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";} +.modal-footer:after{clear:both;} +.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;} +.modal-footer .btn-group .btn+.btn{margin-left:-1px;} +.dropdown{position:relative;} +.dropdown-toggle{*margin-bottom:-3px;} +.dropdown-toggle:active,.open .dropdown-toggle{outline:0;} +.caret{display:inline-block;width:0;height:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"";} +.dropdown .caret{margin-top:8px;margin-left:2px;} +.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);} +.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.pull-right{right:0;left:auto;} +.dropdown-menu .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;} +.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333333;white-space:nowrap;} +.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;} +.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);} +.dropdown.open .dropdown-menu{display:block;} +.pull-right .dropdown-menu{left:auto;right:0;} +.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";} +.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;} +.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.accordion{margin-bottom:18px;} +.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.accordion-heading{border-bottom:0;} +.accordion-heading .accordion-toggle{display:block;padding:8px 15px;} +.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;} +.carousel{position:relative;margin-bottom:18px;line-height:1;} +.carousel-inner{overflow:hidden;width:100%;position:relative;} +.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;} +.carousel .item>img{display:block;line-height:1;} +.carousel .active,.carousel .next,.carousel .prev{display:block;} +.carousel .active{left:0;} +.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;} +.carousel .next{left:100%;} +.carousel .prev{left:-100%;} +.carousel .next.left,.carousel .prev.right{left:0;} +.carousel .active.left{left:-100%;} +.carousel .active.right{left:100%;} +.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;} +.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);} +.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);} +.carousel-caption h4,.carousel-caption p{color:#ffffff;} +.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);} +.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;} +.pull-right{float:right;} +.pull-left{float:left;} +.hide{display:none;} +.show{display:block;} +.invisible{visibility:hidden;} +.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;} +.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;} +.hidden{display:none;visibility:hidden;} +.visible-phone{display:none;} +.visible-tablet{display:none;} +.visible-desktop{display:block;} +.hidden-phone{display:block;} +.hidden-tablet{display:block;} +.hidden-desktop{display:none;} +@media (max-width:767px){.visible-phone{display:block;} .hidden-phone{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (min-width:768px) and (max-width:979px){.visible-tablet{display:block;} .hidden-tablet{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:767px){body{padding-left:20px;padding-right:20px;} .navbar-fixed-top{margin-left:-20px;margin-right:-20px;} .container{width:auto;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;} .thumbnails [class*="span"]{width:auto;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:99.999999993%;} .row-fluid > .span11{width:91.436464082%;} .row-fluid > .span10{width:82.87292817100001%;} .row-fluid > .span9{width:74.30939226%;} .row-fluid > .span8{width:65.74585634900001%;} .row-fluid > .span7{width:57.182320438000005%;} .row-fluid > .span6{width:48.618784527%;} .row-fluid > .span5{width:40.055248616%;} .row-fluid > .span4{width:31.491712705%;} .row-fluid > .span3{width:22.928176794%;} .row-fluid > .span2{width:14.364640883%;} .row-fluid > .span1{width:5.801104972%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (max-width:979px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav .nav-header{color:#999999;text-shadow:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;overflow:visible !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:100%;} .row-fluid > .span11{width:91.45299145300001%;} .row-fluid > .span10{width:82.905982906%;} .row-fluid > .span9{width:74.358974359%;} .row-fluid > .span8{width:65.81196581200001%;} .row-fluid > .span7{width:57.264957265%;} .row-fluid > .span6{width:48.717948718%;} .row-fluid > .span5{width:40.170940171000005%;} .row-fluid > .span4{width:31.623931624%;} .row-fluid > .span3{width:23.076923077%;} .row-fluid > .span2{width:14.529914530000001%;} .row-fluid > .span1{width:5.982905983%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} diff --git a/askbot/skins/default/media/bootstrap/img/glyphicons-halflings-white.png b/askbot/skins/default/media/bootstrap/img/glyphicons-halflings-white.png Binary files differnew file mode 100644 index 00000000..a20760bf --- /dev/null +++ b/askbot/skins/default/media/bootstrap/img/glyphicons-halflings-white.png diff --git a/askbot/skins/default/media/bootstrap/img/glyphicons-halflings.png b/askbot/skins/default/media/bootstrap/img/glyphicons-halflings.png Binary files differnew file mode 100644 index 00000000..92d4445d --- /dev/null +++ b/askbot/skins/default/media/bootstrap/img/glyphicons-halflings.png diff --git a/askbot/skins/default/media/bootstrap/js/bootstrap.js b/askbot/skins/default/media/bootstrap/js/bootstrap.js new file mode 100644 index 00000000..d2d9200d --- /dev/null +++ b/askbot/skins/default/media/bootstrap/js/bootstrap.js @@ -0,0 +1,1737 @@ +/* =================================================== + * bootstrap-transition.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#transitions + * =================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + +!function( $ ) { + + $(function () { + + "use strict" + + /* CSS TRANSITION SUPPORT (https://gist.github.com/373874) + * ======================================================= */ + + $.support.transition = (function () { + var thisBody = document.body || document.documentElement + , thisStyle = thisBody.style + , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined + + return support && { + end: (function () { + var transitionEnd = "TransitionEnd" + if ( $.browser.webkit ) { + transitionEnd = "webkitTransitionEnd" + } else if ( $.browser.mozilla ) { + transitionEnd = "transitionend" + } else if ( $.browser.opera ) { + transitionEnd = "oTransitionEnd" + } + return transitionEnd + }()) + } + })() + + }) + +}( window.jQuery ); +/* ========================================================= + * bootstrap-modal.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#modals + * ========================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================= */ + + +!function( $ ){ + + "use strict" + + /* MODAL CLASS DEFINITION + * ====================== */ + + var Modal = function ( content, options ) { + this.options = options + this.$element = $(content) + .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) + } + + Modal.prototype = { + + constructor: Modal + + , toggle: function () { + return this[!this.isShown ? 'show' : 'hide']() + } + + , show: function () { + var that = this + + if (this.isShown) return + + $('body').addClass('modal-open') + + this.isShown = true + this.$element.trigger('show') + + escape.call(this) + backdrop.call(this, function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + !that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position + + that.$element + .show() + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element.addClass('in') + + transition ? + that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) : + that.$element.trigger('shown') + + }) + } + + , hide: function ( e ) { + e && e.preventDefault() + + if (!this.isShown) return + + var that = this + this.isShown = false + + $('body').removeClass('modal-open') + + escape.call(this) + + this.$element + .trigger('hide') + .removeClass('in') + + $.support.transition && this.$element.hasClass('fade') ? + hideWithTransition.call(this) : + hideModal.call(this) + } + + } + + + /* MODAL PRIVATE METHODS + * ===================== */ + + function hideWithTransition() { + var that = this + , timeout = setTimeout(function () { + that.$element.off($.support.transition.end) + hideModal.call(that) + }, 500) + + this.$element.one($.support.transition.end, function () { + clearTimeout(timeout) + hideModal.call(that) + }) + } + + function hideModal( that ) { + this.$element + .hide() + .trigger('hidden') + + backdrop.call(this) + } + + function backdrop( callback ) { + var that = this + , animate = this.$element.hasClass('fade') ? 'fade' : '' + + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') + .appendTo(document.body) + + if (this.options.backdrop != 'static') { + this.$backdrop.click($.proxy(this.hide, this)) + } + + if (doAnimate) this.$backdrop[0].offsetWidth // force reflow + + this.$backdrop.addClass('in') + + doAnimate ? + this.$backdrop.one($.support.transition.end, callback) : + callback() + + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass('in') + + $.support.transition && this.$element.hasClass('fade')? + this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) : + removeBackdrop.call(this) + + } else if (callback) { + callback() + } + } + + function removeBackdrop() { + this.$backdrop.remove() + this.$backdrop = null + } + + function escape() { + var that = this + if (this.isShown && this.options.keyboard) { + $(document).on('keyup.dismiss.modal', function ( e ) { + e.which == 27 && that.hide() + }) + } else if (!this.isShown) { + $(document).off('keyup.dismiss.modal') + } + } + + + /* MODAL PLUGIN DEFINITION + * ======================= */ + + $.fn.modal = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('modal') + , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option) + if (!data) $this.data('modal', (data = new Modal(this, options))) + if (typeof option == 'string') data[option]() + else if (options.show) data.show() + }) + } + + $.fn.modal.defaults = { + backdrop: true + , keyboard: true + , show: true + } + + $.fn.modal.Constructor = Modal + + + /* MODAL DATA-API + * ============== */ + + $(function () { + $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) { + var $this = $(this), href + , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data()) + + e.preventDefault() + $target.modal(option) + }) + }) + +}( window.jQuery ); +/* ============================================================ + * bootstrap-dropdown.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#dropdowns + * ============================================================ + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + + +!function( $ ){ + + "use strict" + + /* DROPDOWN CLASS DEFINITION + * ========================= */ + + var toggle = '[data-toggle="dropdown"]' + , Dropdown = function ( element ) { + var $el = $(element).on('click.dropdown.data-api', this.toggle) + $('html').on('click.dropdown.data-api', function () { + $el.parent().removeClass('open') + }) + } + + Dropdown.prototype = { + + constructor: Dropdown + + , toggle: function ( e ) { + var $this = $(this) + , selector = $this.attr('data-target') + , $parent + , isActive + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + $parent = $(selector) + $parent.length || ($parent = $this.parent()) + + isActive = $parent.hasClass('open') + + clearMenus() + !isActive && $parent.toggleClass('open') + + return false + } + + } + + function clearMenus() { + $(toggle).parent().removeClass('open') + } + + + /* DROPDOWN PLUGIN DEFINITION + * ========================== */ + + $.fn.dropdown = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('dropdown') + if (!data) $this.data('dropdown', (data = new Dropdown(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.dropdown.Constructor = Dropdown + + + /* APPLY TO STANDARD DROPDOWN ELEMENTS + * =================================== */ + + $(function () { + $('html').on('click.dropdown.data-api', clearMenus) + $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) + }) + +}( window.jQuery ); +/* ============================================================= + * bootstrap-scrollspy.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#scrollspy + * ============================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================== */ + +!function ( $ ) { + + "use strict" + + /* SCROLLSPY CLASS DEFINITION + * ========================== */ + + function ScrollSpy( element, options) { + var process = $.proxy(this.process, this) + , $element = $(element).is('body') ? $(window) : $(element) + , href + this.options = $.extend({}, $.fn.scrollspy.defaults, options) + this.$scrollElement = $element.on('scroll.scroll.data-api', process) + this.selector = (this.options.target + || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + || '') + ' .nav li > a' + this.$body = $('body').on('click.scroll.data-api', this.selector, process) + this.refresh() + this.process() + } + + ScrollSpy.prototype = { + + constructor: ScrollSpy + + , refresh: function () { + this.targets = this.$body + .find(this.selector) + .map(function () { + var href = $(this).attr('href') + return /^#\w/.test(href) && $(href).length ? href : null + }) + + this.offsets = $.map(this.targets, function (id) { + return $(id).position().top + }) + } + + , process: function () { + var scrollTop = this.$scrollElement.scrollTop() + this.options.offset + , offsets = this.offsets + , targets = this.targets + , activeTarget = this.activeTarget + , i + + for (i = offsets.length; i--;) { + activeTarget != targets[i] + && scrollTop >= offsets[i] + && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) + && this.activate( targets[i] ) + } + } + + , activate: function (target) { + var active + + this.activeTarget = target + + this.$body + .find(this.selector).parent('.active') + .removeClass('active') + + active = this.$body + .find(this.selector + '[href="' + target + '"]') + .parent('li') + .addClass('active') + + if ( active.parent('.dropdown-menu') ) { + active.closest('li.dropdown').addClass('active') + } + } + + } + + + /* SCROLLSPY PLUGIN DEFINITION + * =========================== */ + + $.fn.scrollspy = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('scrollspy') + , options = typeof option == 'object' && option + if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.scrollspy.Constructor = ScrollSpy + + $.fn.scrollspy.defaults = { + offset: 10 + } + + + /* SCROLLSPY DATA-API + * ================== */ + + $(function () { + $('[data-spy="scroll"]').each(function () { + var $spy = $(this) + $spy.scrollspy($spy.data()) + }) + }) + +}( window.jQuery ); +/* ======================================================== + * bootstrap-tab.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#tabs + * ======================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================== */ + + +!function( $ ){ + + "use strict" + + /* TAB CLASS DEFINITION + * ==================== */ + + var Tab = function ( element ) { + this.element = $(element) + } + + Tab.prototype = { + + constructor: Tab + + , show: function () { + var $this = this.element + , $ul = $this.closest('ul:not(.dropdown-menu)') + , selector = $this.attr('data-target') + , previous + , $target + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + if ( $this.parent('li').hasClass('active') ) return + + previous = $ul.find('.active a').last()[0] + + $this.trigger({ + type: 'show' + , relatedTarget: previous + }) + + $target = $(selector) + + this.activate($this.parent('li'), $ul) + this.activate($target, $target.parent(), function () { + $this.trigger({ + type: 'shown' + , relatedTarget: previous + }) + }) + } + + , activate: function ( element, container, callback) { + var $active = container.find('> .active') + , transition = callback + && $.support.transition + && $active.hasClass('fade') + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + + element.addClass('active') + + if (transition) { + element[0].offsetWidth // reflow for transition + element.addClass('in') + } else { + element.removeClass('fade') + } + + if ( element.parent('.dropdown-menu') ) { + element.closest('li.dropdown').addClass('active') + } + + callback && callback() + } + + transition ? + $active.one($.support.transition.end, next) : + next() + + $active.removeClass('in') + } + } + + + /* TAB PLUGIN DEFINITION + * ===================== */ + + $.fn.tab = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('tab') + if (!data) $this.data('tab', (data = new Tab(this))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.tab.Constructor = Tab + + + /* TAB DATA-API + * ============ */ + + $(function () { + $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { + e.preventDefault() + $(this).tab('show') + }) + }) + +}( window.jQuery ); +/* =========================================================== + * bootstrap-tooltip.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#tooltips + * Inspired by the original jQuery.tipsy by Jason Frame + * =========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + +!function( $ ) { + + "use strict" + + /* TOOLTIP PUBLIC CLASS DEFINITION + * =============================== */ + + var Tooltip = function ( element, options ) { + this.init('tooltip', element, options) + } + + Tooltip.prototype = { + + constructor: Tooltip + + , init: function ( type, element, options ) { + var eventIn + , eventOut + + this.type = type + this.$element = $(element) + this.options = this.getOptions(options) + this.enabled = true + + if (this.options.trigger != 'manual') { + eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus' + eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur' + this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this)) + this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this)) + } + + this.options.selector ? + (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : + this.fixTitle() + } + + , getOptions: function ( options ) { + options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data()) + + if (options.delay && typeof options.delay == 'number') { + options.delay = { + show: options.delay + , hide: options.delay + } + } + + return options + } + + , enter: function ( e ) { + var self = $(e.currentTarget)[this.type](this._options).data(this.type) + + if (!self.options.delay || !self.options.delay.show) { + self.show() + } else { + self.hoverState = 'in' + setTimeout(function() { + if (self.hoverState == 'in') { + self.show() + } + }, self.options.delay.show) + } + } + + , leave: function ( e ) { + var self = $(e.currentTarget)[this.type](this._options).data(this.type) + + if (!self.options.delay || !self.options.delay.hide) { + self.hide() + } else { + self.hoverState = 'out' + setTimeout(function() { + if (self.hoverState == 'out') { + self.hide() + } + }, self.options.delay.hide) + } + } + + , show: function () { + var $tip + , inside + , pos + , actualWidth + , actualHeight + , placement + , tp + + if (this.hasContent() && this.enabled) { + $tip = this.tip() + this.setContent() + + if (this.options.animation) { + $tip.addClass('fade') + } + + placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement + + inside = /in/.test(placement) + + $tip + .remove() + .css({ top: 0, left: 0, display: 'block' }) + .appendTo(inside ? this.$element : document.body) + + pos = this.getPosition(inside) + + actualWidth = $tip[0].offsetWidth + actualHeight = $tip[0].offsetHeight + + switch (inside ? placement.split(' ')[1] : placement) { + case 'bottom': + tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} + break + case 'top': + tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} + break + case 'left': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth} + break + case 'right': + tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width} + break + } + + $tip + .css(tp) + .addClass(placement) + .addClass('in') + } + } + + , setContent: function () { + var $tip = this.tip() + $tip.find('.tooltip-inner').html(this.getTitle()) + $tip.removeClass('fade in top bottom left right') + } + + , hide: function () { + var that = this + , $tip = this.tip() + + $tip.removeClass('in') + + function removeWithAnimation() { + var timeout = setTimeout(function () { + $tip.off($.support.transition.end).remove() + }, 500) + + $tip.one($.support.transition.end, function () { + clearTimeout(timeout) + $tip.remove() + }) + } + + $.support.transition && this.$tip.hasClass('fade') ? + removeWithAnimation() : + $tip.remove() + } + + , fixTitle: function () { + var $e = this.$element + if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { + $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title') + } + } + + , hasContent: function () { + return this.getTitle() + } + + , getPosition: function (inside) { + return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), { + width: this.$element[0].offsetWidth + , height: this.$element[0].offsetHeight + }) + } + + , getTitle: function () { + var title + , $e = this.$element + , o = this.options + + title = $e.attr('data-original-title') + || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) + + title = (title || '').toString().replace(/(^\s*|\s*$)/, "") + + return title + } + + , tip: function () { + return this.$tip = this.$tip || $(this.options.template) + } + + , validate: function () { + if (!this.$element[0].parentNode) { + this.hide() + this.$element = null + this.options = null + } + } + + , enable: function () { + this.enabled = true + } + + , disable: function () { + this.enabled = false + } + + , toggleEnabled: function () { + this.enabled = !this.enabled + } + + , toggle: function () { + this[this.tip().hasClass('in') ? 'hide' : 'show']() + } + + } + + + /* TOOLTIP PLUGIN DEFINITION + * ========================= */ + + $.fn.tooltip = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('tooltip') + , options = typeof option == 'object' && option + if (!data) $this.data('tooltip', (data = new Tooltip(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.tooltip.Constructor = Tooltip + + $.fn.tooltip.defaults = { + animation: true + , delay: 0 + , selector: false + , placement: 'top' + , trigger: 'hover' + , title: '' + , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' + } + +}( window.jQuery ); +/* =========================================================== + * bootstrap-popover.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#popovers + * =========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =========================================================== */ + + +!function( $ ) { + + "use strict" + + var Popover = function ( element, options ) { + this.init('popover', element, options) + } + + /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js + ========================================== */ + + Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, { + + constructor: Popover + + , setContent: function () { + var $tip = this.tip() + , title = this.getTitle() + , content = this.getContent() + + $tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title) + $tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content) + + $tip.removeClass('fade top bottom left right in') + } + + , hasContent: function () { + return this.getTitle() || this.getContent() + } + + , getContent: function () { + var content + , $e = this.$element + , o = this.options + + content = $e.attr('data-content') + || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) + + content = content.toString().replace(/(^\s*|\s*$)/, "") + + return content + } + + , tip: function() { + if (!this.$tip) { + this.$tip = $(this.options.template) + } + return this.$tip + } + + }) + + + /* POPOVER PLUGIN DEFINITION + * ======================= */ + + $.fn.popover = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('popover') + , options = typeof option == 'object' && option + if (!data) $this.data('popover', (data = new Popover(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.popover.Constructor = Popover + + $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, { + placement: 'right' + , content: '' + , template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>' + }) + +}( window.jQuery ); +/* ========================================================== + * bootstrap-alert.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#alerts + * ========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function( $ ){ + + "use strict" + + /* ALERT CLASS DEFINITION + * ====================== */ + + var dismiss = '[data-dismiss="alert"]' + , Alert = function ( el ) { + $(el).on('click', dismiss, this.close) + } + + Alert.prototype = { + + constructor: Alert + + , close: function ( e ) { + var $this = $(this) + , selector = $this.attr('data-target') + , $parent + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + $parent = $(selector) + $parent.trigger('close') + + e && e.preventDefault() + + $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) + + $parent + .trigger('close') + .removeClass('in') + + function removeElement() { + $parent + .trigger('closed') + .remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent.on($.support.transition.end, removeElement) : + removeElement() + } + + } + + + /* ALERT PLUGIN DEFINITION + * ======================= */ + + $.fn.alert = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('alert') + if (!data) $this.data('alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.alert.Constructor = Alert + + + /* ALERT DATA-API + * ============== */ + + $(function () { + $('body').on('click.alert.data-api', dismiss, Alert.prototype.close) + }) + +}( window.jQuery ); +/* ============================================================ + * bootstrap-button.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#buttons + * ============================================================ + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + +!function( $ ){ + + "use strict" + + /* BUTTON PUBLIC CLASS DEFINITION + * ============================== */ + + var Button = function ( element, options ) { + this.$element = $(element) + this.options = $.extend({}, $.fn.button.defaults, options) + } + + Button.prototype = { + + constructor: Button + + , setState: function ( state ) { + var d = 'disabled' + , $el = this.$element + , data = $el.data() + , val = $el.is('input') ? 'val' : 'html' + + state = state + 'Text' + data.resetText || $el.data('resetText', $el[val]()) + + $el[val](data[state] || this.options[state]) + + // push to event loop to allow forms to submit + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d) + }, 0) + } + + , toggle: function () { + var $parent = this.$element.parent('[data-toggle="buttons-radio"]') + + $parent && $parent + .find('.active') + .removeClass('active') + + this.$element.toggleClass('active') + } + + } + + + /* BUTTON PLUGIN DEFINITION + * ======================== */ + + $.fn.button = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('button') + , options = typeof option == 'object' && option + if (!data) $this.data('button', (data = new Button(this, options))) + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + $.fn.button.defaults = { + loadingText: 'loading...' + } + + $.fn.button.Constructor = Button + + + /* BUTTON DATA-API + * =============== */ + + $(function () { + $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') + }) + }) + +}( window.jQuery ); +/* ============================================================= + * bootstrap-collapse.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#collapse + * ============================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + +!function( $ ){ + + "use strict" + + var Collapse = function ( element, options ) { + this.$element = $(element) + this.options = $.extend({}, $.fn.collapse.defaults, options) + + if (this.options["parent"]) { + this.$parent = $(this.options["parent"]) + } + + this.options.toggle && this.toggle() + } + + Collapse.prototype = { + + constructor: Collapse + + , dimension: function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + , show: function () { + var dimension = this.dimension() + , scroll = $.camelCase(['scroll', dimension].join('-')) + , actives = this.$parent && this.$parent.find('.in') + , hasData + + if (actives && actives.length) { + hasData = actives.data('collapse') + actives.collapse('hide') + hasData || actives.data('collapse', null) + } + + this.$element[dimension](0) + this.transition('addClass', 'show', 'shown') + this.$element[dimension](this.$element[0][scroll]) + + } + + , hide: function () { + var dimension = this.dimension() + this.reset(this.$element[dimension]()) + this.transition('removeClass', 'hide', 'hidden') + this.$element[dimension](0) + } + + , reset: function ( size ) { + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + [dimension](size || 'auto') + [0].offsetWidth + + this.$element[size ? 'addClass' : 'removeClass']('collapse') + + return this + } + + , transition: function ( method, startEvent, completeEvent ) { + var that = this + , complete = function () { + if (startEvent == 'show') that.reset() + that.$element.trigger(completeEvent) + } + + this.$element + .trigger(startEvent) + [method]('in') + + $.support.transition && this.$element.hasClass('collapse') ? + this.$element.one($.support.transition.end, complete) : + complete() + } + + , toggle: function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + } + + /* COLLAPSIBLE PLUGIN DEFINITION + * ============================== */ + + $.fn.collapse = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('collapse') + , options = typeof option == 'object' && option + if (!data) $this.data('collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.defaults = { + toggle: true + } + + $.fn.collapse.Constructor = Collapse + + + /* COLLAPSIBLE DATA-API + * ==================== */ + + $(function () { + $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { + var $this = $(this), href + , target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + , option = $(target).data('collapse') ? 'toggle' : $this.data() + $(target).collapse(option) + }) + }) + +}( window.jQuery ); +/* ========================================================== + * bootstrap-carousel.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#carousel + * ========================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + + +!function( $ ){ + + "use strict" + + /* CAROUSEL CLASS DEFINITION + * ========================= */ + + var Carousel = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, $.fn.carousel.defaults, options) + this.options.slide && this.slide(this.options.slide) + this.options.pause == 'hover' && this.$element + .on('mouseenter', $.proxy(this.pause, this)) + .on('mouseleave', $.proxy(this.cycle, this)) + } + + Carousel.prototype = { + + cycle: function () { + this.interval = setInterval($.proxy(this.next, this), this.options.interval) + return this + } + + , to: function (pos) { + var $active = this.$element.find('.active') + , children = $active.parent().children() + , activePos = children.index($active) + , that = this + + if (pos > (children.length - 1) || pos < 0) return + + if (this.sliding) { + return this.$element.one('slid', function () { + that.to(pos) + }) + } + + if (activePos == pos) { + return this.pause().cycle() + } + + return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos])) + } + + , pause: function () { + clearInterval(this.interval) + this.interval = null + return this + } + + , next: function () { + if (this.sliding) return + return this.slide('next') + } + + , prev: function () { + if (this.sliding) return + return this.slide('prev') + } + + , slide: function (type, next) { + var $active = this.$element.find('.active') + , $next = next || $active[type]() + , isCycling = this.interval + , direction = type == 'next' ? 'left' : 'right' + , fallback = type == 'next' ? 'first' : 'last' + , that = this + + this.sliding = true + + isCycling && this.pause() + + $next = $next.length ? $next : this.$element.find('.item')[fallback]() + + if ($next.hasClass('active')) return + + if (!$.support.transition && this.$element.hasClass('slide')) { + this.$element.trigger('slide') + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger('slid') + } else { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + this.$element.trigger('slide') + this.$element.one($.support.transition.end, function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { that.$element.trigger('slid') }, 0) + }) + } + + isCycling && this.cycle() + + return this + } + + } + + + /* CAROUSEL PLUGIN DEFINITION + * ========================== */ + + $.fn.carousel = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('carousel') + , options = typeof option == 'object' && option + if (!data) $this.data('carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (typeof option == 'string' || (option = options.slide)) data[option]() + else data.cycle() + }) + } + + $.fn.carousel.defaults = { + interval: 5000 + , pause: 'hover' + } + + $.fn.carousel.Constructor = Carousel + + + /* CAROUSEL DATA-API + * ================= */ + + $(function () { + $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) { + var $this = $(this), href + , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data()) + $target.carousel(options) + e.preventDefault() + }) + }) + +}( window.jQuery ); +/* ============================================================= + * bootstrap-typeahead.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#typeahead + * ============================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + +!function( $ ){ + + "use strict" + + var Typeahead = function ( element, options ) { + this.$element = $(element) + this.options = $.extend({}, $.fn.typeahead.defaults, options) + this.matcher = this.options.matcher || this.matcher + this.sorter = this.options.sorter || this.sorter + this.highlighter = this.options.highlighter || this.highlighter + this.$menu = $(this.options.menu).appendTo('body') + this.source = this.options.source + this.shown = false + this.listen() + } + + Typeahead.prototype = { + + constructor: Typeahead + + , select: function () { + var val = this.$menu.find('.active').attr('data-value') + this.$element.val(val) + this.$element.change(); + return this.hide() + } + + , show: function () { + var pos = $.extend({}, this.$element.offset(), { + height: this.$element[0].offsetHeight + }) + + this.$menu.css({ + top: pos.top + pos.height + , left: pos.left + }) + + this.$menu.show() + this.shown = true + return this + } + + , hide: function () { + this.$menu.hide() + this.shown = false + return this + } + + , lookup: function (event) { + var that = this + , items + , q + + this.query = this.$element.val() + + if (!this.query) { + return this.shown ? this.hide() : this + } + + items = $.grep(this.source, function (item) { + if (that.matcher(item)) return item + }) + + items = this.sorter(items) + + if (!items.length) { + return this.shown ? this.hide() : this + } + + return this.render(items.slice(0, this.options.items)).show() + } + + , matcher: function (item) { + return ~item.toLowerCase().indexOf(this.query.toLowerCase()) + } + + , sorter: function (items) { + var beginswith = [] + , caseSensitive = [] + , caseInsensitive = [] + , item + + while (item = items.shift()) { + if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item) + else if (~item.indexOf(this.query)) caseSensitive.push(item) + else caseInsensitive.push(item) + } + + return beginswith.concat(caseSensitive, caseInsensitive) + } + + , highlighter: function (item) { + return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) { + return '<strong>' + match + '</strong>' + }) + } + + , render: function (items) { + var that = this + + items = $(items).map(function (i, item) { + i = $(that.options.item).attr('data-value', item) + i.find('a').html(that.highlighter(item)) + return i[0] + }) + + items.first().addClass('active') + this.$menu.html(items) + return this + } + + , next: function (event) { + var active = this.$menu.find('.active').removeClass('active') + , next = active.next() + + if (!next.length) { + next = $(this.$menu.find('li')[0]) + } + + next.addClass('active') + } + + , prev: function (event) { + var active = this.$menu.find('.active').removeClass('active') + , prev = active.prev() + + if (!prev.length) { + prev = this.$menu.find('li').last() + } + + prev.addClass('active') + } + + , listen: function () { + this.$element + .on('blur', $.proxy(this.blur, this)) + .on('keypress', $.proxy(this.keypress, this)) + .on('keyup', $.proxy(this.keyup, this)) + + if ($.browser.webkit || $.browser.msie) { + this.$element.on('keydown', $.proxy(this.keypress, this)) + } + + this.$menu + .on('click', $.proxy(this.click, this)) + .on('mouseenter', 'li', $.proxy(this.mouseenter, this)) + } + + , keyup: function (e) { + switch(e.keyCode) { + case 40: // down arrow + case 38: // up arrow + break + + case 9: // tab + case 13: // enter + if (!this.shown) return + this.select() + break + + case 27: // escape + if (!this.shown) return + this.hide() + break + + default: + this.lookup() + } + + e.stopPropagation() + e.preventDefault() + } + + , keypress: function (e) { + if (!this.shown) return + + switch(e.keyCode) { + case 9: // tab + case 13: // enter + case 27: // escape + e.preventDefault() + break + + case 38: // up arrow + e.preventDefault() + this.prev() + break + + case 40: // down arrow + e.preventDefault() + this.next() + break + } + + e.stopPropagation() + } + + , blur: function (e) { + var that = this + setTimeout(function () { that.hide() }, 150) + } + + , click: function (e) { + e.stopPropagation() + e.preventDefault() + this.select() + } + + , mouseenter: function (e) { + this.$menu.find('.active').removeClass('active') + $(e.currentTarget).addClass('active') + } + + } + + + /* TYPEAHEAD PLUGIN DEFINITION + * =========================== */ + + $.fn.typeahead = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('typeahead') + , options = typeof option == 'object' && option + if (!data) $this.data('typeahead', (data = new Typeahead(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.typeahead.defaults = { + source: [] + , items: 8 + , menu: '<ul class="typeahead dropdown-menu"></ul>' + , item: '<li><a href="#"></a></li>' + } + + $.fn.typeahead.Constructor = Typeahead + + + /* TYPEAHEAD DATA-API + * ================== */ + + $(function () { + $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) { + var $this = $(this) + if ($this.data('typeahead')) return + e.preventDefault() + $this.typeahead($this.data()) + }) + }) + +}( window.jQuery );
\ No newline at end of file diff --git a/askbot/skins/default/media/bootstrap/js/bootstrap.min.js b/askbot/skins/default/media/bootstrap/js/bootstrap.min.js new file mode 100644 index 00000000..edfee401 --- /dev/null +++ b/askbot/skins/default/media/bootstrap/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/** +* Bootstrap.js by @fat & @mdo +* plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-scrollspy.js, bootstrap-tab.js, bootstrap-tooltip.js, bootstrap-popover.js, bootstrap-alert.js, bootstrap-button.js, bootstrap-collapse.js, bootstrap-carousel.js, bootstrap-typeahead.js +* Copyright 2012 Twitter, Inc. +* http://www.apache.org/licenses/LICENSE-2.0.txt +*/ +!function(a){a(function(){a.support.transition=function(){var b=document.body||document.documentElement,c=b.style,d=c.transition!==undefined||c.WebkitTransition!==undefined||c.MozTransition!==undefined||c.MsTransition!==undefined||c.OTransition!==undefined;return d&&{end:function(){var b="TransitionEnd";return a.browser.webkit?b="webkitTransitionEnd":a.browser.mozilla?b="transitionend":a.browser.opera&&(b="oTransitionEnd"),b}()}}()})}(window.jQuery),!function(a){function c(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),d.call(b)},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),d.call(b)})}function d(a){this.$element.hide().trigger("hidden"),e.call(this)}function e(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('<div class="modal-backdrop '+d+'" />').appendTo(document.body),this.options.backdrop!="static"&&this.$backdrop.click(a.proxy(this.hide,this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),e?this.$backdrop.one(a.support.transition.end,b):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,a.proxy(f,this)):f.call(this)):b&&b()}function f(){this.$backdrop.remove(),this.$backdrop=null}function g(){var b=this;this.isShown&&this.options.keyboard?a(document).on("keyup.dismiss.modal",function(a){a.which==27&&b.hide()}):this.isShown||a(document).off("keyup.dismiss.modal")}var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this))};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this;if(this.isShown)return;a("body").addClass("modal-open"),this.isShown=!0,this.$element.trigger("show"),g.call(this),e.call(this,function(){var c=a.support.transition&&b.$element.hasClass("fade");!b.$element.parent().length&&b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in"),c?b.$element.one(a.support.transition.end,function(){b.$element.trigger("shown")}):b.$element.trigger("shown")})},hide:function(b){b&&b.preventDefault();if(!this.isShown)return;var e=this;this.isShown=!1,a("body").removeClass("modal-open"),g.call(this),this.$element.trigger("hide").removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?c.call(this):d.call(this)}},a.fn.modal=function(c){return this.each(function(){var d=a(this),e=d.data("modal"),f=a.extend({},a.fn.modal.defaults,d.data(),typeof c=="object"&&c);e||d.data("modal",e=new b(this,f)),typeof c=="string"?e[c]():f.show&&e.show()})},a.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},a.fn.modal.Constructor=b,a(function(){a("body").on("click.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("modal")?"toggle":a.extend({},e.data(),c.data());b.preventDefault(),e.modal(f)})})}(window.jQuery),!function(a){function d(){a(b).parent().removeClass("open")}var b='[data-toggle="dropdown"]',c=function(b){var c=a(b).on("click.dropdown.data-api",this.toggle);a("html").on("click.dropdown.data-api",function(){c.parent().removeClass("open")})};c.prototype={constructor:c,toggle:function(b){var c=a(this),e=c.attr("data-target"),f,g;return e||(e=c.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,"")),f=a(e),f.length||(f=c.parent()),g=f.hasClass("open"),d(),!g&&f.toggleClass("open"),!1}},a.fn.dropdown=function(b){return this.each(function(){var d=a(this),e=d.data("dropdown");e||d.data("dropdown",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.dropdown.Constructor=c,a(function(){a("html").on("click.dropdown.data-api",d),a("body").on("click.dropdown.data-api",b,c.prototype.toggle)})}(window.jQuery),!function(a){function b(b,c){var d=a.proxy(this.process,this),e=a(b).is("body")?a(window):a(b),f;this.options=a.extend({},a.fn.scrollspy.defaults,c),this.$scrollElement=e.on("scroll.scroll.data-api",d),this.selector=(this.options.target||(f=a(b).attr("href"))&&f.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=a("body").on("click.scroll.data-api",this.selector,d),this.refresh(),this.process()}b.prototype={constructor:b,refresh:function(){this.targets=this.$body.find(this.selector).map(function(){var b=a(this).attr("href");return/^#\w/.test(b)&&a(b).length?b:null}),this.offsets=a.map(this.targets,function(b){return a(b).position().top})},process:function(){var a=this.$scrollElement.scrollTop()+this.options.offset,b=this.offsets,c=this.targets,d=this.activeTarget,e;for(e=b.length;e--;)d!=c[e]&&a>=b[e]&&(!b[e+1]||a<=b[e+1])&&this.activate(c[e])},activate:function(a){var b;this.activeTarget=a,this.$body.find(this.selector).parent(".active").removeClass("active"),b=this.$body.find(this.selector+'[href="'+a+'"]').parent("li").addClass("active"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active")}},a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("scrollspy"),f=typeof c=="object"&&c;e||d.data("scrollspy",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.defaults={offset:10},a(function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),!function(a){var b=function(b){this.element=a(b)};b.prototype={constructor:b,show:function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target"),e,f;d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;e=c.find(".active a").last()[0],b.trigger({type:"show",relatedTarget:e}),f=a(d),this.activate(b.parent("li"),c),this.activate(f,f.parent(),function(){b.trigger({type:"shown",relatedTarget:e})})},activate:function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g):g(),e.removeClass("in")}},a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("tab");e||d.data("tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a(function(){a("body").on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})})}(window.jQuery),!function(a){var b=function(a,b){this.init("tooltip",a,b)};b.prototype={constructor:b,init:function(b,c,d){var e,f;this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.enabled=!0,this.options.trigger!="manual"&&(e=this.options.trigger=="hover"?"mouseenter":"focus",f=this.options.trigger=="hover"?"mouseleave":"blur",this.$element.on(e,this.options.selector,a.proxy(this.enter,this)),this.$element.on(f,this.options.selector,a.proxy(this.leave,this))),this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(b){return b=a.extend({},a.fn[this.type].defaults,b,this.$element.data()),b.delay&&typeof b.delay=="number"&&(b.delay={show:b.delay,hide:b.delay}),b},enter:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.show?c.show():(c.hoverState="in",setTimeout(function(){c.hoverState=="in"&&c.show()},c.options.delay.show))},leave:function(b){var c=a(b.currentTarget)[this.type](this._options).data(this.type);!c.options.delay||!c.options.delay.hide?c.hide():(c.hoverState="out",setTimeout(function(){c.hoverState=="out"&&c.hide()},c.options.delay.hide))},show:function(){var a,b,c,d,e,f,g;if(this.hasContent()&&this.enabled){a=this.tip(),this.setContent(),this.options.animation&&a.addClass("fade"),f=typeof this.options.placement=="function"?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,b=/in/.test(f),a.remove().css({top:0,left:0,display:"block"}).appendTo(b?this.$element:document.body),c=this.getPosition(b),d=a[0].offsetWidth,e=a[0].offsetHeight;switch(b?f.split(" ")[1]:f){case"bottom":g={top:c.top+c.height,left:c.left+c.width/2-d/2};break;case"top":g={top:c.top-e,left:c.left+c.width/2-d/2};break;case"left":g={top:c.top+c.height/2-e/2,left:c.left-d};break;case"right":g={top:c.top+c.height/2-e/2,left:c.left+c.width}}a.css(g).addClass(f).addClass("in")}},setContent:function(){var a=this.tip();a.find(".tooltip-inner").html(this.getTitle()),a.removeClass("fade in top bottom left right")},hide:function(){function d(){var b=setTimeout(function(){c.off(a.support.transition.end).remove()},500);c.one(a.support.transition.end,function(){clearTimeout(b),c.remove()})}var b=this,c=this.tip();c.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d():c.remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||typeof a.attr("data-original-title")!="string")&&a.attr("data-original-title",a.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(b){return a.extend({},b?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||(typeof c.title=="function"?c.title.call(b[0]):c.title),a=(a||"").toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip=this.$tip||a(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(){this[this.tip().hasClass("in")?"hide":"show"]()}},a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("tooltip"),f=typeof c=="object"&&c;e||d.data("tooltip",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.tooltip.Constructor=b,a.fn.tooltip.defaults={animation:!0,delay:0,selector:!1,placement:"top",trigger:"hover",title:"",template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'}}(window.jQuery),!function(a){var b=function(a,b){this.init("popover",a,b)};b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype,{constructor:b,setContent:function(){var b=this.tip(),c=this.getTitle(),d=this.getContent();b.find(".popover-title")[a.type(c)=="object"?"append":"html"](c),b.find(".popover-content > *")[a.type(d)=="object"?"append":"html"](d),b.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var a,b=this.$element,c=this.options;return a=b.attr("data-content")||(typeof c.content=="function"?c.content.call(b[0]):c.content),a=a.toString().replace(/(^\s*|\s*$)/,""),a},tip:function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip}}),a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("popover"),f=typeof c=="object"&&c;e||d.data("popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.defaults=a.extend({},a.fn.tooltip.defaults,{placement:"right",content:"",template:'<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'})}(window.jQuery),!function(a){var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype={constructor:c,close:function(b){function f(){e.trigger("closed").remove()}var c=a(this),d=c.attr("data-target"),e;d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),e=a(d),e.trigger("close"),b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger("close").removeClass("in"),a.support.transition&&e.hasClass("fade")?e.on(a.support.transition.end,f):f()}},a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("alert");e||d.data("alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a(function(){a("body").on("click.alert.data-api",b,c.prototype.close)})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.button.defaults,c)};b.prototype={constructor:b,setState:function(a){var b="disabled",c=this.$element,d=c.data(),e=c.is("input")?"val":"html";a+="Text",d.resetText||c.data("resetText",c[e]()),c[e](d[a]||this.options[a]),setTimeout(function(){a=="loadingText"?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},toggle:function(){var a=this.$element.parent('[data-toggle="buttons-radio"]');a&&a.find(".active").removeClass("active"),this.$element.toggleClass("active")}},a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("button"),f=typeof c=="object"&&c;e||d.data("button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.defaults={loadingText:"loading..."},a.fn.button.Constructor=b,a(function(){a("body").on("click.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle")})})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.collapse.defaults,c),this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.prototype={constructor:b,dimension:function(){var a=this.$element.hasClass("width");return a?"width":"height"},show:function(){var b=this.dimension(),c=a.camelCase(["scroll",b].join("-")),d=this.$parent&&this.$parent.find(".in"),e;d&&d.length&&(e=d.data("collapse"),d.collapse("hide"),e||d.data("collapse",null)),this.$element[b](0),this.transition("addClass","show","shown"),this.$element[b](this.$element[0][c])},hide:function(){var a=this.dimension();this.reset(this.$element[a]()),this.transition("removeClass","hide","hidden"),this.$element[a](0)},reset:function(a){var b=this.dimension();return this.$element.removeClass("collapse")[b](a||"auto")[0].offsetWidth,this.$element[a?"addClass":"removeClass"]("collapse"),this},transition:function(b,c,d){var e=this,f=function(){c=="show"&&e.reset(),e.$element.trigger(d)};this.$element.trigger(c)[b]("in"),a.support.transition&&this.$element.hasClass("collapse")?this.$element.one(a.support.transition.end,f):f()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}},a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("collapse"),f=typeof c=="object"&&c;e||d.data("collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.defaults={toggle:!0},a.fn.collapse.Constructor=b,a(function(){a("body").on("click.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e).data("collapse")?"toggle":c.data();a(e).collapse(f)})})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.carousel.defaults,c),this.options.slide&&this.slide(this.options.slide),this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.prototype={cycle:function(){return this.interval=setInterval(a.proxy(this.next,this),this.options.interval),this},to:function(b){var c=this.$element.find(".active"),d=c.parent().children(),e=d.index(c),f=this;if(b>d.length-1||b<0)return;return this.sliding?this.$element.one("slid",function(){f.to(b)}):e==b?this.pause().cycle():this.slide(b>e?"next":"prev",a(d[b]))},pause:function(){return clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(b,c){var d=this.$element.find(".active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();if(e.hasClass("active"))return;return!a.support.transition&&this.$element.hasClass("slide")?(this.$element.trigger("slide"),d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")):(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),this.$element.trigger("slide"),this.$element.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)})),f&&this.cycle(),this}},a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("carousel"),f=typeof c=="object"&&c;e||d.data("carousel",e=new b(this,f)),typeof c=="number"?e.to(c):typeof c=="string"||(c=f.slide)?e[c]():e.cycle()})},a.fn.carousel.defaults={interval:5e3,pause:"hover"},a.fn.carousel.Constructor=b,a(function(){a("body").on("click.carousel.data-api","[data-slide]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=!e.data("modal")&&a.extend({},e.data(),c.data());e.carousel(f),b.preventDefault()})})}(window.jQuery),!function(a){var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.typeahead.defaults,c),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.$menu=a(this.options.menu).appendTo("body"),this.source=this.options.source,this.shown=!1,this.listen()};b.prototype={constructor:b,select:function(){var a=this.$menu.find(".active").attr("data-value");return this.$element.val(a),this.$element.change(),this.hide()},show:function(){var b=a.extend({},this.$element.offset(),{height:this.$element[0].offsetHeight});return this.$menu.css({top:b.top+b.height,left:b.left}),this.$menu.show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(b){var c=this,d,e;return this.query=this.$element.val(),this.query?(d=a.grep(this.source,function(a){if(c.matcher(a))return a}),d=this.sorter(d),d.length?this.render(d.slice(0,this.options.items)).show():this.shown?this.hide():this):this.shown?this.hide():this},matcher:function(a){return~a.toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(a){var b=[],c=[],d=[],e;while(e=a.shift())e.toLowerCase().indexOf(this.query.toLowerCase())?~e.indexOf(this.query)?c.push(e):d.push(e):b.push(e);return b.concat(c,d)},highlighter:function(a){return a.replace(new RegExp("("+this.query+")","ig"),function(a,b){return"<strong>"+b+"</strong>"})},render:function(b){var c=this;return b=a(b).map(function(b,d){return b=a(c.options.item).attr("data-value",d),b.find("a").html(c.highlighter(d)),b[0]}),b.first().addClass("active"),this.$menu.html(b),this},next:function(b){var c=this.$menu.find(".active").removeClass("active"),d=c.next();d.length||(d=a(this.$menu.find("li")[0])),d.addClass("active")},prev:function(a){var b=this.$menu.find(".active").removeClass("active"),c=b.prev();c.length||(c=this.$menu.find("li").last()),c.addClass("active")},listen:function(){this.$element.on("blur",a.proxy(this.blur,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),(a.browser.webkit||a.browser.msie)&&this.$element.on("keydown",a.proxy(this.keypress,this)),this.$menu.on("click",a.proxy(this.click,this)).on("mouseenter","li",a.proxy(this.mouseenter,this))},keyup:function(a){switch(a.keyCode){case 40:case 38:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}a.stopPropagation(),a.preventDefault()},keypress:function(a){if(!this.shown)return;switch(a.keyCode){case 9:case 13:case 27:a.preventDefault();break;case 38:a.preventDefault(),this.prev();break;case 40:a.preventDefault(),this.next()}a.stopPropagation()},blur:function(a){var b=this;setTimeout(function(){b.hide()},150)},click:function(a){a.stopPropagation(),a.preventDefault(),this.select()},mouseenter:function(b){this.$menu.find(".active").removeClass("active"),a(b.currentTarget).addClass("active")}},a.fn.typeahead=function(c){return this.each(function(){var d=a(this),e=d.data("typeahead"),f=typeof c=="object"&&c;e||d.data("typeahead",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.typeahead.defaults={source:[],items:8,menu:'<ul class="typeahead dropdown-menu"></ul>',item:'<li><a href="#"></a></li>'},a.fn.typeahead.Constructor=b,a(function(){a("body").on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(b){var c=a(this);if(c.data("typeahead"))return;b.preventDefault(),c.typeahead(c.data())})})}(window.jQuery)
\ No newline at end of file diff --git a/askbot/skins/default/media/style/lib_style.less b/askbot/skins/default/media/style/lib_style.less index bedd8c60..63389526 100644 --- a/askbot/skins/default/media/style/lib_style.less +++ b/askbot/skins/default/media/style/lib_style.less @@ -14,7 +14,7 @@ @body-font:Arial; /* "Trebuchet MS", sans-serif;*/ @sort-font:Georgia, serif; -@main-font:'Yanone Kaffeesatz', sans-serif; +@main-font:'Yanone Kaffeesatz', Arial, sans-serif; @secondary-font:Arial; /* Buttons */ diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css index 5752b502..3d53eba8 100644 --- a/askbot/skins/default/media/style/style.css +++ b/askbot/skins/default/media/style/style.css @@ -3,6 +3,22 @@ /* Variables for Colors*/ /* Variables for fonts*/ /* "Trebuchet MS", sans-serif;*/ +/* Buttons */ +.button-style-hover { + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + text-decoration: none; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; +} +/* General styles for gradients */ /* Receive exactly positions for background Sprite */ /* CSS3 Elements */ /* Library of predifined less functions styles */ @@ -148,7 +164,7 @@ a:hover { } h1 { font-size: 24px; - padding: 10px 0 5px 0px; + padding: 0px 0 5px 0px; } /* ----- Extra space above for messages ----- */ body.user-messages { @@ -179,7 +195,7 @@ body.user-messages { text-align: center; background-color: #f5dd69; border-top: #fff 1px solid; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .notify p.notification { margin-top: 6px; @@ -206,7 +222,7 @@ body.user-messages { #header { margin-top: 0px; background: #16160f; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .content-wrapper { /* wrapper positioning class */ @@ -295,6 +311,9 @@ body.user-messages { #metaNav #navUsers { background: -125px -5px url(../images/sprites.png) no-repeat; } +#metaNav #navGroups { + background: -125px -5px url(../images/sprites.png) no-repeat; +} #metaNav #navBadges { background: -210px -5px url(../images/sprites.png) no-repeat; } @@ -318,7 +337,7 @@ body.user-messages { border-bottom: #d3d3c2 1px solid; border-top: #fcfcfc 1px solid; margin-bottom: 10px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } #secondaryHeader #homeButton { border-right: #afaf9e 1px solid; @@ -427,28 +446,49 @@ body.anon #searchBar .searchInputCancelable { #askButton { /* check blocks/secondary_header.html and widgets/ask_button.html*/ - background: url(../images/bigbutton.png) repeat-x bottom; line-height: 44px; - text-align: center; + margin-top: 6px; + float: right; + text-transform: uppercase; width: 200px; height: 42px; font-size: 23px; + text-align: center; + text-decoration: none; + cursor: pointer; color: #4a757f; - margin-top: 7px; - float: right; - text-transform: uppercase; - border-radius: 5px; - -ms-border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - -khtml-border-radius: 5px; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + border-radius: 4px; + -ms-border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; -webkit-box-shadow: 1px 1px 2px #636363; -moz-box-shadow: 1px 1px 2px #636363; box-shadow: 1px 1px 2px #636363; } #askButton:hover { + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); text-decoration: none; - background: url(../images/bigbutton.png) repeat-x top; text-shadow: 0px 1px 0px #c6d9dd; -moz-text-shadow: 0px 1px 0px #c6d9dd; -webkit-text-shadow: 0px 1px 0px #c6d9dd; @@ -481,6 +521,8 @@ body.anon #searchBar .searchInputCancelable { } .box p { margin-bottom: 4px; + color: #707070; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .box p.info-box-follow-up-links { text-align: right; @@ -497,14 +539,15 @@ body.anon #searchBar .searchInputCancelable { color: #656565; padding-right: 10px; margin-bottom: 10px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + width: 190px; } .box h3 { color: #4a757f; font-size: 18px; text-align: left; font-weight: normal; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; padding-left: 0px; } .box .contributorback { @@ -516,12 +559,13 @@ body.anon #searchBar .searchInputCancelable { display: block; float: right; text-align: left; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; width: 80px; margin-right: 18px; } -.box #displayTagFilterControl label { - /*Especial width just for the display tag filter box in index page*/ +.box #displayTagFilterControl label, +.box #emailTagFilterControl label { + /*Especial width just for the tag filter boxes in index page*/ width: 160px; } @@ -547,57 +591,108 @@ body.anon #searchBar .searchInputCancelable { font-size: 15px; } .box .inputs #interestingTagInput, -.box .inputs #ignoredTagInput { +.box .inputs #ignoredTagInput, +.box .inputs #subscribedTagInput, +.box .inputs #ab-tag-search { width: 153px; padding-left: 5px; border: #c9c9b5 1px solid; height: 25px; } +.box .inputs #ab-tag-search { + width: 135px; +} .box .inputs #interestingTagAdd, -.box .inputs #ignoredTagAdd { - background: url(../images/small-button-blue.png) repeat-x top; +.box .inputs #ignoredTagAdd, +.box .inputs #subscribedTagAdd, +.box .inputs #ab-tag-search-add { border: 0; - color: #4a757f; font-weight: bold; - font-size: 12px; + margin-top: -2px; width: 30px; height: 27px; - margin-top: -2px; + font-size: 14px; + text-align: center; + text-decoration: none; cursor: pointer; + color: #4a757f; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + border-radius: 4px; + -ms-border-radius: 4px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; + -webkit-box-shadow: 1px 1px 2px #636363; + -moz-box-shadow: 1px 1px 2px #636363; + box-shadow: 1px 1px 2px #636363; border-radius: 4px; -ms-border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; -khtml-border-radius: 4px; - text-shadow: 0px 1px 0px #e6f6fa; - -moz-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-box-shadow: 1px 1px 2px #808080; - -moz-box-shadow: 1px 1px 2px #808080; - box-shadow: 1px 1px 2px #808080; } .box .inputs #interestingTagAdd:hover, -.box .inputs #ignoredTagAdd:hover { - background: url(../images/small-button-blue.png) repeat-x bottom; +.box .inputs #ignoredTagAdd:hover, +.box .inputs #ab-tag-search-add:hover { + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + text-decoration: none; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; +} +.box .inputs #ab-tag-search-add { + width: 47px; + margin-left: 3px; } .box img.gravatar { margin: 1px; } .box a.followed, .box a.follow { - background: url(../images/medium-button.png) top repeat-x; - height: 34px; line-height: 34px; - text-align: center; border: 0; - font-family: 'Yanone Kaffeesatz', sans-serif; - color: #4a757f; font-weight: normal; - font-size: 21px; margin-top: 3px; display: block; width: 120px; + height: 34px; + font-size: 21px; + text-align: center; text-decoration: none; + cursor: pointer; + color: #4a757f; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); border-radius: 4px; -ms-border-radius: 4px; -moz-border-radius: 4px; @@ -611,8 +706,18 @@ body.anon #searchBar .searchInputCancelable { } .box a.followed:hover, .box a.follow:hover { + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); text-decoration: none; - background: url(../images/medium-button.png) bottom repeat-x; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; text-shadow: 0px 1px 0px #c6d9dd; -moz-text-shadow: 0px 1px 0px #c6d9dd; -webkit-text-shadow: 0px 1px 0px #c6d9dd; @@ -638,6 +743,10 @@ body.anon #searchBar .searchInputCancelable { .box .notify-sidebar #question-subscribe-sidebar { margin: 7px 0 0 3px; } +.users-page .box label { + display: inline; + float: none; +} .statsWidget p { color: #707070; font-size: 16px; @@ -730,8 +839,7 @@ body.anon #searchBar .searchInputCancelable { .tabsC .label { float: left; color: #646464; - margin-top: 4px; - margin-right: 5px; + margin: 4px 5px 0px 8px; } .main-page .tabsA .label { margin-left: 8px; @@ -776,14 +884,14 @@ body.anon #searchBar .searchInputCancelable { float: left; margin-bottom: 8px; padding-top: 6px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } #listSearchTags { float: left; margin-top: 3px; color: #707070; font-size: 16px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } ul#searchTags { margin-left: 10px; @@ -797,7 +905,7 @@ ul#searchTags { margin: 5px 0 10px 0; padding: 0px; float: left; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .search-tips a { text-decoration: underline; @@ -811,6 +919,9 @@ ul#searchTags { padding: 0; width: 100%; } +.main-page #question-list { + margin-top: 10px; +} .short-summary { position: relative; filter: inherit; @@ -829,7 +940,7 @@ ul#searchTags { padding-left: 0; margin-bottom: 6px; display: block; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .short-summary a { color: #464646; @@ -840,7 +951,7 @@ ul#searchTags { font-family: Arial; padding-right: 4px; } -.short-summary .userinfo .relativetime, +.short-summary .userinfo .timeago, .short-summary span.anonymous { font-size: 11px; clear: both; @@ -854,12 +965,12 @@ ul#searchTags { .short-summary .counts { float: right; margin: 4px 0 0 5px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .short-summary .counts .item-count { padding: 0px 5px 0px 5px; font-size: 25px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .short-summary .counts .votes div, .short-summary .counts .views div, @@ -1049,7 +1160,7 @@ ul#related-tags { ul.tags li { float: left; display: block; - margin: 0 8px 0 0; + margin: 0 8px 8px 0; padding: 0; height: 20px; } @@ -1173,7 +1284,7 @@ ul#related-tags li { /* ----- Ask and Edit Question Form template----- */ .section-title { color: #7ea9b3; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; font-weight: bold; font-size: 24px; } @@ -1199,7 +1310,7 @@ ul#related-tags li { margin: 0px; padding: 0px 0 0 5px; border: #cce6ec 3px solid; - width: 725px; + width: 719px; } .ask-page div#question-list, .edit-question-page div#question-list { @@ -1261,14 +1372,28 @@ ul#related-tags li { .ask-page input.submit, .edit-question-page input.submit { float: left; - background: url(../images/medium-button.png) top repeat-x; - height: 34px; - border: 0; - font-family: 'Yanone Kaffeesatz', sans-serif; - color: #4a757f; font-weight: normal; - font-size: 21px; margin-top: 3px; + width: 160px; + height: 34px; + font-size: 21px; + text-align: center; + text-decoration: none; + cursor: pointer; + color: #4a757f; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); border-radius: 4px; -ms-border-radius: 4px; -moz-border-radius: 4px; @@ -1282,33 +1407,60 @@ ul#related-tags li { #fmanswer input.submit:hover, .ask-page input.submit:hover, .edit-question-page input.submit:hover { + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); text-decoration: none; - background: url(../images/medium-button.png) bottom repeat-x; text-shadow: 0px 1px 0px #c6d9dd; -moz-text-shadow: 0px 1px 0px #c6d9dd; -webkit-text-shadow: 0px 1px 0px #c6d9dd; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; +} +.wmd-container { + border: #cce6ec 3px solid; +} +.users-page .wmd-container { + width: 200px; +} +.ask-page .wmd-container, +.question-page .wmd-container, +.edit-question-page .wmd-container, +.edit-answer-page .wmd-container { + width: 723px; +} +.ask-page #editor, +.question-page #editor, +.edit-question-page #editor, +.edit-answer-page #editor { + width: 710px; + padding: 6px; } #editor { - /*adjustment for editor preview*/ + /* adjustment for editor preview */ + display: block; font-size: 100%; min-height: 200px; line-height: 18px; margin: 0; - border-left: #cce6ec 3px solid; - border-bottom: #cce6ec 3px solid; - border-right: #cce6ec 3px solid; - border-top: 0; - padding: 10px; - margin-bottom: 10px; - width: 717px; + border: 0; +} +.users-page #editor { + width: 192px; } #id_title { width: 100%; } .wmd-preview { - margin: 3px 0 5px 0; - padding: 6px; + margin: 0; + padding: 5px; background-color: #F5F5F5; min-height: 20px; overflow: auto; @@ -1320,6 +1472,9 @@ ul#related-tags li { line-height: 1.4; font-size: 14px; } +.wmd-preview p:last-child { + margin-bottom: 0; +} .wmd-preview pre { background-color: #E7F1F8; } @@ -1329,6 +1484,9 @@ ul#related-tags li { .wmd-preview IMG { max-width: 600px; } +.user-page .wmd-buttons { + width: 725px; +} .preview-toggle { width: 100%; color: #b6a475; @@ -1383,7 +1541,7 @@ ul#related-tags li { margin: 0px; padding: 0px 0 0 5px; border: #cce6ec 3px solid; - width: 725px; + width: 719px; margin-bottom: 10px; } .edit-question-page #id_summary, @@ -1403,7 +1561,7 @@ ul#related-tags li { /* ----- Question template ----- */ .question-page h1 { padding-top: 0px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .question-page h1 a { color: #464646; @@ -1421,7 +1579,7 @@ ul#related-tags li { margin-left: 0px !important; } .question-page p.rss a { - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; vertical-align: top; } .question-page .question-content { @@ -1586,7 +1744,7 @@ ul#related-tags li { } .question-page #questionCount { float: left; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; line-height: 15px; } .question-page .question-img-upvote, @@ -1632,13 +1790,11 @@ ul#related-tags li { color: #7ea9b3; width: 200px; float: left; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .question-page .comments { font-size: 12px; clear: both; - /* A small hack to solve 1px problem on webkit browsers */ - } .question-page .comments div.controls { clear: both; @@ -1688,11 +1844,6 @@ ul#related-tags li { padding-top: 3px; border: #cce6ec 3px solid; } -@media screen and (-webkit-min-device-pixel-ratio: 0) { - .question-page .comments textarea { - padding-left: 3px !important; - } -} .question-page .comments input { margin-left: 10px; margin-top: 1px; @@ -1700,31 +1851,49 @@ ul#related-tags li { width: 100px; } .question-page .comments button { - background: url(../images/small-button-blue.png) repeat-x top; - border: 0; - color: #4a757f; - font-family: Arial; - font-size: 13px; - width: 100px; - font-weight: bold; - height: 27px; line-height: 25px; margin-bottom: 5px; + width: 100px; + height: 27px; + font-size: 12px; + text-align: center; + text-decoration: none; cursor: pointer; + color: #4a757f; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); border-radius: 4px; -ms-border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; -khtml-border-radius: 4px; - text-shadow: 0px 1px 0px #e6f6fa; - -moz-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-box-shadow: 1px 1px 2px #808080; - -moz-box-shadow: 1px 1px 2px #808080; - box-shadow: 1px 1px 2px #808080; + -webkit-box-shadow: 1px 1px 2px #636363; + -moz-box-shadow: 1px 1px 2px #636363; + box-shadow: 1px 1px 2px #636363; + font-family: Arial; + font-weight: bold; } .question-page .comments button:hover { - background: url(../images/small-button-blue.png) bottom repeat-x; + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + text-decoration: none; text-shadow: 0px 1px 0px #c6d9dd; -moz-text-shadow: 0px 1px 0px #c6d9dd; -webkit-text-shadow: 0px 1px 0px #c6d9dd; @@ -1865,12 +2034,17 @@ ul#related-tags li { text-align: center; padding-top: 2px; margin: 10px 10px 0px 3px; + /* smalls IE fixes */ + + *margin: 0; + *height: 210px; + *width: 30px; } .question-page .vote-buttons IMG { cursor: pointer; } .question-page .vote-number { - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; padding: 0px 0 5px 0; font-size: 25px; font-weight: bold; @@ -1948,7 +2122,7 @@ ul#related-tags li { margin-top: 10px; } .question-page #fmanswer h2 { - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; color: #7ea9b3; font-size: 24px; } @@ -1993,7 +2167,6 @@ ul#related-tags li { /* -----Content pages, Login, About, FAQ, Users----- */ .openid-signin, .meta, -.users-page, .user-profile-edit-page { font-size: 13px; line-height: 1.3; @@ -2001,7 +2174,6 @@ ul#related-tags li { } .openid-signin p, .meta p, -.users-page p, .user-profile-edit-page p { font-size: 13px; color: #707070; @@ -2012,7 +2184,6 @@ ul#related-tags li { } .openid-signin h2, .meta h2, -.users-page h2, .user-profile-edit-page h2 { color: #525252; padding-left: 0px; @@ -2070,35 +2241,55 @@ ul#related-tags li { .users-page input.submit, .user-profile-edit-page input.submit, .user-profile-page input.submit { - background: url(../images/small-button-blue.png) repeat-x top; - border: 0; - color: #4a757f; - font-weight: bold; - font-size: 13px; - font-family: Arial; - height: 26px; + font-weight: normal; margin: 5px 0px; width: 100px; + height: 26px; + font-size: 15px; + text-align: center; + text-decoration: none; cursor: pointer; + color: #4a757f; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); border-radius: 4px; -ms-border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; -khtml-border-radius: 4px; - text-shadow: 0px 1px 0px #e6f6fa; - -moz-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-box-shadow: 1px 1px 2px #808080; - -moz-box-shadow: 1px 1px 2px #808080; - box-shadow: 1px 1px 2px #808080; + -webkit-box-shadow: 1px 1px 2px #636363; + -moz-box-shadow: 1px 1px 2px #636363; + box-shadow: 1px 1px 2px #636363; + font-family: Arial; } .openid-signin input.submit:hover, .meta input.submit:hover, .users-page input.submit:hover, .user-profile-edit-page input.submit:hover, .user-profile-page input.submit:hover { - background: url(../images/small-button-blue.png) repeat-x bottom; + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); text-decoration: none; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; } .openid-signin .cancel, .meta .cancel, @@ -2115,6 +2306,9 @@ ul#related-tags li { .user-profile-page .cancel:hover { background: url(../images/small-button-cancel.png) repeat-x bottom !important; } +.openid-signin form { + margin-bottom: 5px; +} #email-input-fs, #local_login_buttons, #password-fs, @@ -2145,34 +2339,55 @@ ul#related-tags li { #local_login_buttons .submit-b, #password-fs .submit-b, #openid-fs .submit-b { - background: url(../images/small-button-blue.png) repeat-x top; - border: 0; - color: #4a757f; - font-weight: bold; - font-size: 13px; - font-family: Arial; + width: 100px; height: 24px; - margin-top: -2px; - padding-left: 10px; - padding-right: 10px; + font-size: 15px; + text-align: center; + text-decoration: none; cursor: pointer; + color: #4a757f; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); border-radius: 4px; -ms-border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; -khtml-border-radius: 4px; - text-shadow: 0px 1px 0px #e6f6fa; - -moz-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-box-shadow: 1px 1px 2px #808080; - -moz-box-shadow: 1px 1px 2px #808080; - box-shadow: 1px 1px 2px #808080; + -webkit-box-shadow: 1px 1px 2px #636363; + -moz-box-shadow: 1px 1px 2px #636363; + box-shadow: 1px 1px 2px #636363; + font-family: Arial; + font-weight: bold; + padding-right: 10px; + border: 0; } #email-input-fs .submit-b:hover, #local_login_buttons .submit-b:hover, #password-fs .submit-b:hover, #openid-fs .submit-b:hover { - background: url(../images/small-button-blue.png) repeat-x bottom; + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + text-decoration: none; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; } .openid-input { background: url(../images/openid.gif) no-repeat; @@ -2199,20 +2414,15 @@ ul#related-tags li { font-size: 120%; } /* People page */ -.tabBar-user { - width: 375px; -} +/*.users-page .tabBar{ + width:375px; +}*/ .user { - padding: 5px; + padding: 5px 10px 5px 0; line-height: 140%; width: 166px; - border: #eee 1px solid; + height: 32px; margin-bottom: 5px; - border-radius: 3px; - -ms-border-radius: 3px; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - -khtml-border-radius: 3px; } .user .user-micro-info { color: #525252; @@ -2280,7 +2490,7 @@ a:hover.medal { } .user-profile-page h2 { padding: 10px 0px 10px 0px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } .user-details { font-size: 13px; @@ -2302,31 +2512,52 @@ a:hover.medal { .follow-toggle, .submit { border: 0 !important; - color: #4a757f; font-weight: bold; - font-size: 12px; - height: 26px; line-height: 26px; margin-top: -2px; - font-size: 15px; + width: 100px; + height: 26px; + font-size: 12px; + text-align: center; + text-decoration: none; cursor: pointer; - font-family: 'Yanone Kaffeesatz', sans-serif; - background: url(../images/small-button-blue.png) repeat-x top; + color: #4a757f; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; + border-top: #eaf2f3 1px solid; + background-color: #d1e2e5; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#d1e2e5), color-stop(25%, #d1e2e5), to(#a9c2c7)); + background-image: -webkit-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -moz-linear-gradient(top, #d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -ms-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: -o-linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); + background-image: linear-gradient(#d1e2e5, #d1e2e5 25%, #a9c2c7); border-radius: 4px; -ms-border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; -khtml-border-radius: 4px; - text-shadow: 0px 1px 0px #e6f6fa; - -moz-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-text-shadow: 0px 1px 0px #e6f6fa; - -webkit-box-shadow: 1px 1px 2px #808080; - -moz-box-shadow: 1px 1px 2px #808080; - box-shadow: 1px 1px 2px #808080; + -webkit-box-shadow: 1px 1px 2px #636363; + -moz-box-shadow: 1px 1px 2px #636363; + box-shadow: 1px 1px 2px #636363; } .follow-toggle:hover, .submit:hover { - background: url(../images/small-button-blue.png) repeat-x bottom; + background-color: #cde5e9; + background-repeat: no-repeat; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#cde5e9), color-stop(25%, #cde5e9), to(#94b3ba)); + background-image: -webkit-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -moz-linear-gradient(top, #cde5e9, #cde5e9 25%, #94b3ba); + background-image: -ms-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: -o-linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + background-image: linear-gradient(#cde5e9, #cde5e9 25%, #94b3ba); + text-decoration: none; + text-shadow: 0px 1px 0px #c6d9dd; + -moz-text-shadow: 0px 1px 0px #c6d9dd; + -webkit-text-shadow: 0px 1px 0px #c6d9dd; text-decoration: none !important; } .follow-toggle .follow { @@ -2346,13 +2577,13 @@ a:hover.medal { display: none; } .count { - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; font-size: 200%; font-weight: 700; color: #777777; } .scoreNumber { - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; font-size: 35px; font-weight: 800; color: #777; @@ -2463,7 +2694,7 @@ a:hover.medal { color: #525252; } .revision h3 { - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; font-size: 21px; padding-left: 0px; } @@ -2570,7 +2801,7 @@ ins { padding: 6px 0 0 0; background: #16160f; font-size: 16px; - font-family: 'Yanone Kaffeesatz', sans-serif; + font-family: 'Yanone Kaffeesatz', Arial, sans-serif; } #ground p { margin-bottom: 0; @@ -2714,7 +2945,7 @@ span.form-error { padding: 0px; margin: 0px; } -.relativetime { +.timeago { font-weight: bold; text-decoration: none; } @@ -2935,6 +3166,9 @@ button::-moz-focus-inner { -khtml-border-radius: 5px; -webkit-border-radius: 5px; } +.list-table { + border-spacing: 0; +} .list-table td { vertical-align: top; } @@ -3246,3 +3480,110 @@ body.anon.lang-es #searchBar .searchInput { body.anon.lang-es #searchBar .searchInputCancelable { width: 390px; } +/* user groups */ +#user-groups ul { + margin-bottom: 0px; +} +#user-groups .delete-icon { + float: none; + display: inline; + color: #525252; + padding: 0 3px 0 3px; + background: #ccc; + border-radius: 4px; + line-height: inherit; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + -webkit-border-radius: 4px; +} +#user-groups .delete-icon:hover { + color: white; + background: #b32f2f; +} +.users-page .wmd-prompt-dialog { + background: #ccc; +} +.group-wiki .content > p:last-child { + margin-bottom: 5px; +} +.group-wiki .group-logo { + float: left; + margin: 0 5px 3px 0; +} +.group-wiki .follow-toggle.group-join-btn { + width: 150px; + margin: 4px auto 10px auto; + display: block; +} +.group-wiki .controls { + margin: 0 0 10px 0; +} +img.group-logo { + height: 60px; + /* important to align with the line spacing */ + +} +#groups-list { + margin-left: 0px; +} +#groups-list li { + display: inline; + list-style-type: none; + list-style-position: inside; + float: left; + text-align: center; +} +#groups-list .group-logo, +#groups-list .group-name { + display: block; +} +#reject-edit-modal input, +#reject-edit-modal textarea { + width: 514px; +} +input.tipped-input, +textarea.tipped-input { + padding-left: 5px; +} +.tipped-input.blank { + color: #707070; +} +.select-box { + margin: 0; +} +.select-box li { + list-style-type: none; + list-style-position: inside; + padding-left: 7px; + font-size: 14px; + line-height: 25px; +} +.select-box li.selected, +.select-box li.selected:hover { + background-color: #fcf8e3; + color: #c09853; +} +.select-box li:hover { + background-color: #cecece; + color: white; +} +/* fixes for bootstrap */ +.caret { + margin-bottom: 7px; +} +.btn-group { + text-align: left; +} +.btn-toolbar { + margin: 0; +} +.modal-footer { + text-align: left; +} +.modal p { + font-size: 14px; +} +.modal-body > textarea { + width: 515px; + margin-bottom: 0px; +} diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index 0d1e8112..a98bcdbe 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -151,7 +151,7 @@ a:hover { h1 { font-size: 24px; - padding: 10px 0 5px 0px; + padding: 0px 0 5px 0px; } @@ -313,6 +313,10 @@ body.user-messages { .sprites(-125px,-5px) } + #navGroups{ + .sprites(-125px,-5px) + } + #navBadges{ .sprites(-210px,-5px) } @@ -504,6 +508,8 @@ body.anon { p { margin-bottom: 4px; + color: @info-text; + font-family:@main-font; } p.info-box-follow-up-links { @@ -548,7 +554,8 @@ body.anon { margin-right:18px; } - #displayTagFilterControl label { /*Especial width just for the display tag filter box in index page*/ + #displayTagFilterControl label, + #emailTagFilterControl label { /*Especial width just for the tag filter boxes in index page*/ width:160px; } @@ -576,23 +583,37 @@ body.anon { } .inputs{ - #interestingTagInput, #ignoredTagInput{ + #interestingTagInput, + #ignoredTagInput, + #subscribedTagInput, + #ab-tag-search { width:153px; padding-left:5px; border:#c9c9b5 1px solid; height:25px; } - #interestingTagAdd, #ignoredTagAdd{ + #ab-tag-search { + width: 135px; + } + #interestingTagAdd, + #ignoredTagAdd, + #subscribedTagAdd, + #ab-tag-search-add { border:0; font-weight:bold; margin-top:-2px; - .button-style(30px,27px,12px); + .button-style(30px, 27px, 14px); .rounded-corners(4px); - .text-shadow(0px,1px,0px,#E6F6FA); - } - #interestingTagAdd:hover, #ignoredTagAdd:hover{ + } + #interestingTagAdd:hover, + #ignoredTagAdd:hover, + #ab-tag-search-add:hover { .button-style-hover; } + #ab-tag-search-add { + width: 47px; + margin-left: 3px; + } } img.gravatar { @@ -643,6 +664,11 @@ body.anon { } } +.users-page .box label { + display: inline; + float: none; +} + .statsWidget p{ color:@info-text; font-size:16px; @@ -672,7 +698,7 @@ body.anon { #tips{ li{ - color:@info-text; + color:@info-text; font-size:13px; list-style-image: url(../images/tips.png); } @@ -742,8 +768,7 @@ body.anon { .tabsA .label, .tabsC .label { float: left; color: #646464; - margin-top:4px; - margin-right:5px; + margin:4px 5px 0px 8px; } .main-page .tabsA .label { @@ -837,6 +862,10 @@ ul#searchTags { width: 100%; } +.main-page #question-list { + margin-top: 10px; +} + .short-summary { position: relative; filter: inherit; @@ -870,7 +899,7 @@ ul#searchTags { } - .userinfo .relativetime, span.anonymous + .userinfo .timeago, span.anonymous { font-size: 11px; clear:both; @@ -1263,11 +1292,11 @@ ul#related-tags li { margin: 0px; padding: 0px 0 0 5px; border:#cce6ec 3px solid; - width:725px; + width:719px; } } -.ask-page, .edit-question-page{ +.ask-page, .edit-question-page { div#question-list { float: none; @@ -1338,24 +1367,37 @@ ul#related-tags li { .button-style-hover; .text-shadow(0px, 1px, 0px, #c6d9dd) } -#editor { /*adjustment for editor preview*/ + +.wmd-container { + border:#cce6ec 3px solid; +} +.users-page .wmd-container { + width: 200px; +} +.ask-page, +.question-page, +.edit-question-page, +.edit-answer-page { + .wmd-container { + width: 723px; + } + #editor { + width: 710px; + padding: 6px; + } +} + +#editor { /* adjustment for editor preview */ + display: block; font-size: 100%; min-height: 200px; line-height: 18px; - margin:0; - border-left:#cce6ec 3px solid; - border-bottom:#cce6ec 3px solid; - border-right:#cce6ec 3px solid; - border-top:0; - padding:10px; - margin-bottom:10px; - width:710px; + margin: 0; + border: 0; } -@media screen and (-webkit-min-device-pixel-ratio:0){ - #editor{ - width:717px; - } +.users-page #editor { + width: 192px; } #id_title { @@ -1363,8 +1405,8 @@ ul#related-tags li { } .wmd-preview { - margin: 3px 0 5px 0; - padding: 6px; + margin: 0; + padding: 5px; background-color: #F5F5F5; min-height: 20px; overflow: auto; @@ -1372,9 +1414,13 @@ ul#related-tags li { font-family:@body-font; p{ - margin-bottom:14px; - line-height:1.4; - font-size:14px; + margin-bottom: 14px; + line-height: 1.4; + font-size: 14px; + } + + p:last-child{ + margin-bottom: 0; } } @@ -1391,6 +1437,10 @@ ul#related-tags li { max-width: 600px; } +.user-page .wmd-buttons { + width: 725px; +} + .preview-toggle { width: 100%; color: #b6a475; /*letter-spacing:1px;*/ @@ -1443,7 +1493,7 @@ ul#related-tags li { margin: 0px; padding: 0px 0 0 5px; border:#cce6ec 3px solid; - width:725px; + width: 719px; margin-bottom:10px; } #id_summary{ @@ -1754,13 +1804,6 @@ ul#related-tags li { border:#cce6ec 3px solid; } - /* A small hack to solve 1px problem on webkit browsers */ - @media screen and (-webkit-min-device-pixel-ratio:0){ - textarea{ - padding-left:3px !important; - } - } - input { margin-left: 10px; margin-top: 1px; @@ -2072,7 +2115,6 @@ ul#related-tags li { .openid-signin, .meta, -.users-page, .user-profile-edit-page, { font-size:13px; @@ -2138,6 +2180,10 @@ ul#related-tags li { } } +.openid-signin form { + margin-bottom: 5px; +} + #email-input-fs,#local_login_buttons,#password-fs,#openid-fs{ margin-top:10px; #id_email,#id_username,#id_password{ @@ -2192,17 +2238,16 @@ ul#related-tags li { /* People page */ -.tabBar-user{ +/*.users-page .tabBar{ width:375px; -} +}*/ .user { - padding: 5px; + padding: 5px 10px 5px 0; line-height: 140%; width: 166px; - border:#eee 1px solid; + height: 32px; margin-bottom:5px; - .rounded-corners(3px); .user-micro-info{ color:@info-text-dark; } @@ -2742,7 +2787,7 @@ span.form-error { margin: 0px; } -.relativetime { +.timeago { font-weight: bold; text-decoration: none; } @@ -3010,8 +3055,11 @@ button::-moz-focus-inner { -webkit-border-radius: 5px; } -.list-table td { - vertical-align: top; +.list-table { + td { + vertical-align: top; + } + border-spacing: 0; } /* these need to go */ @@ -3324,12 +3372,128 @@ body.anon.lang-es { } } } -a.re_expand{ - color: #616161; - text-decoration:none; + +/* user groups */ +#user-groups ul { + margin-bottom: 0px; } +#user-groups .delete-icon { + float: none; + display: inline; + color: #525252; + padding: 0 3px 0 3px; + background: #ccc; + border-radius: 4px; + line-height:inherit; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + -webkit-border-radius: 4px; +} +#user-groups .delete-icon:hover { + color: white; + background: #b32f2f; +} + +.users-page { + .wmd-prompt-dialog { + background: #ccc; + } +} + +.group-wiki { + .content { + > p:last-child { + margin-bottom: 5px; + } + } + .group-logo { + float: left; + margin: 0 5px 3px 0; + } + .follow-toggle.group-join-btn { + width: 150px; + margin: 4px auto 10px auto; + display: block; + } + .controls { + margin: 0 0 10px 0; + } +} + +img.group-logo { + height: 60px;/* important to align with the line spacing */ +} + +#groups-list { + margin-left: 0px; + li { + display: inline; + list-style-type: none; + list-style-position: inside; + float: left; + text-align: center; + } + .group-logo, .group-name { + display: block; + } +} + + +#reject-edit-modal { + input, textarea { + width: 514px; + } +} + +input.tipped-input, +textarea.tipped-input { + padding-left: 5px; +} + +.tipped-input.blank { + color: @info-text; +} + +.select-box { + + margin: 0; + + li { + list-style-type: none; + list-style-position: inside; + padding-left: 7px; + font-size: 14px; + line-height: 25px; + } + li.selected, + li.selected:hover { + background-color: #fcf8e3; + color: #c09853; + } -a.re_expand .re_content{ - display:none; - margin-left:77px; + li:hover { + background-color: #cecece; + color: white; + } +} + +/* fixes for bootstrap */ +.caret { + margin-bottom: 7px; +} +.btn-group { + text-align: left; +} +.btn-toolbar { + margin: 0; +} +.modal-footer { + text-align: left; +} +.modal p { + font-size: 14px; +} +.modal-body > textarea { + width: 515px; + margin-bottom: 0px; } diff --git a/askbot/skins/default/templates/base.html b/askbot/skins/default/templates/base.html index bc0a8d6c..da771a41 100644 --- a/askbot/skins/default/templates/base.html +++ b/askbot/skins/default/templates/base.html @@ -6,6 +6,7 @@ <title>{% block title %}{% endblock %} - {{ settings.APP_TITLE|escape }}</title> {% include "meta/html_head_meta.html" %} <link rel="shortcut icon" href="{{ settings.SITE_FAVICON|media }}" /> + {% block before_css %}{% endblock %} {% include "meta/html_head_stylesheets.html" %} {% block forestyle %}{% endblock %} {% include "meta/html_head_javascript.html" %} diff --git a/askbot/skins/default/templates/groups.html b/askbot/skins/default/templates/groups.html new file mode 100644 index 00000000..eda0c3ff --- /dev/null +++ b/askbot/skins/default/templates/groups.html @@ -0,0 +1,18 @@ +{% import "macros.html" as macros %} +{% extends 'one_column_body.html' %} +{% block title %}{% trans %}Groups{% endtrans %}{% endblock %} +{% block content %} + <h1 class="section-title">{% trans %}Groups{% endtrans %}</h1> + {% if can_edit %} + <p id="group-add-tip"> + {% trans %}Tip: to create a new group - please go to some user profile and add the new group there. That user will be the first member of the group{% endtrans %} + </p> + {% endif %} + <ul id="groups-list"> + {% for group in groups %} + <li> + {{ macros.user_group(group) }} + </li> + {% endfor %} + </ul> +{% endblock %} diff --git a/askbot/skins/default/templates/instant_notification.html b/askbot/skins/default/templates/instant_notification.html index 92799a96..cd6e5427 100644 --- a/askbot/skins/default/templates/instant_notification.html +++ b/askbot/skins/default/templates/instant_notification.html @@ -1,41 +1,6 @@ -{% trans %}<p>Dear {{receiving_user_name}},</p>{% endtrans %} - {% if update_type == 'question_comment' %} +{{ reply_separator }} +<div>{{ content_preview }}</div> {% trans %} -<p>{{update_author_name}} left a <a href="{{post_url}}">new comment</a>:</p> -{% endtrans %} - {% endif %} - {% if update_type == 'answer_comment' %} -{% trans %} -<p>{{update_author_name}} left a <a href="{{post_url}}">new comment</a></p> -{% endtrans %} - {% endif %} - {% if update_type == 'new_answer' %} -{% trans %} -<p>{{update_author_name}} answered a question -<a href="{{post_url}}">{{origin_post_title}}</a></p> -{% endtrans %} - {% endif %} - {% if update_type == 'new_question' %} -{% trans %} -<p>{{update_author_name}} posted a new question -<a href="{{post_url}}">{{origin_post_title}}</a></p> -{% endtrans %} - {% endif %} - {%if update_type == 'answer_update' %} -{% trans %} -<p>{{update_author_name}} updated an answer to the question -<a href="{{post_url}}">{{origin_post_title}}</a></p> -{% endtrans %} - {% endif %} - {% if update_type == 'question_update' %} -{% trans %} -<p>{{update_author_name}} updated a question -<a href="{{post_url}}">{{origin_post_title}}</a></p> -{% endtrans %} - {% endif %} -<p></p> -{% trans %} -<div>{{content_preview}}</div> <p>Please note - you can easily <a href="{{user_subscriptions_url}}">change</a> how often you receive these notifications or unsubscribe. Thank you for your interest in our forum!</p> {% endtrans %} diff --git a/askbot/skins/default/templates/instant_notification_reply_by_email.html b/askbot/skins/default/templates/instant_notification_reply_by_email.html deleted file mode 100644 index ffb43110..00000000 --- a/askbot/skins/default/templates/instant_notification_reply_by_email.html +++ /dev/null @@ -1,14 +0,0 @@ - -{% if can_reply %} -{% trans %} -{# Don't change the following line in the template. #} -======= Reply above this line. ====-=-= -{% endtrans %} -{% else %} -{% trans %} -You can post an answer or a comment by replying to email notifications. To do that -you need {{reply_by_email_karma_threshold}} karma, you have {{receiving_user_karma}} karma. -{% endtrans %} -{% endif %} - -{% include 'instant_notification.html' %}
\ No newline at end of file diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index 1c6b925c..4bae1e45 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -23,6 +23,29 @@ </div> {%- endmacro -%} +{%- macro inbox_post_snippet(response, inbox_section) -%} +<div id="re_{{response.id}}" class="re{% if response.is_new %} new highlight{% else %} seen{% endif %}"> + <input type="checkbox" /> + <div class="face"> + {{ gravatar(response.user, 48) }} + </div> + <a style="font-size:12px" href="{{ response.user.get_absolute_url() }}">{{ response.user.username }}</a> + <a style="text-decoration:none;" href="{{ response.response_url }}"> + {{ response.response_type }} + ({{ timeago(response.timestamp) }}):<br/> + {% if inbox_section != 'flags' %} + {{ response.response_snippet }} + {% endif %} + </a> + {% if inbox_section == 'flags' %} + <a class="re_expand" href="{{ response.response_url }}"> + <!--div class="re_snippet">{{ response.response_snippet }}</div--> + <div class="re_content">{{ response.response_content }}</div> + </a> + {% endif %} +</div> +{%- endmacro -%} + {%- macro post_vote_buttons(post = None) -%} <div id="{{post.post_type}}-img-upvote-{{ post.id }}" class="{{post.post_type}}-img-upvote post-vote"> @@ -41,24 +64,37 @@ </script> {%- endmacro -%} -{%- macro post_contributor_avatar_and_credentials(post, user) -%} +{%- macro post_contributor_avatar_and_credentials(post, user, karma_mode = None, badges_mode = None) -%} {% if post.is_anonymous %} <img alt="{% trans %}anonymous user{% endtrans %}" src="{{ '/images/anon.png'|media }} " class="gravatar" width="32" height="32" /> <p>{{ user.get_anonymous_name() }}</p> {% else %} {{ gravatar(user, 32) }} {{ user.get_profile_link()}}{{ user_country_flag(user) }}<br/> - {{ user_score_and_badge_summary(user) }}<br/> + {{ user_score_and_badge_summary(user, karma_mode = karma_mode, badges_mode = badges_mode) }}<br/> {{ user_website_link(user) }} {% endif %} {%- endmacro -%} -{%- macro post_last_updater_and_creator_info(post, min_rep_to_edit_wiki) -%} - {{ post_contributor_info(post, "original_author", post.wiki, min_rep_to_edit_wiki) }} - {{ post_contributor_info(post, "last_updater", post.wiki, min_rep_to_edit_wiki) }} +{%- macro post_last_updater_and_creator_info( + post, min_rep_to_edit_wiki, karma_mode = None, badges_mode = None + ) -%} + {{ post_contributor_info( + post, "original_author", post.wiki, min_rep_to_edit_wiki, + karma_mode = karma_mode, badges_mode = badges_mode + ) + }} + {{ post_contributor_info( + post, "last_updater", post.wiki, min_rep_to_edit_wiki, + karma_mode = karma_mode, badges_mode = badges_mode + ) + }} {%- endmacro -%} -{%- macro post_contributor_info(post, contributor_type, is_wiki, wiki_min_rep) -%} +{%- macro post_contributor_info( + post, contributor_type, is_wiki, wiki_min_rep, + karma_mode = None, badges_mode = None + ) -%} {# there is a whole bunch of trickery here, probably indicative of poor design of the data or methods on data objects #} {% if contributor_type=="original_author" %} @@ -72,7 +108,7 @@ poor design of the data or methods on data objects #} {% else %} {%- trans %}posted{% endtrans %} {% endif %} - <strong>{{post.added_at|diff_date}}</strong> + <strong>{{ timeago(post.added_at) }}</strong> </p> <img width="35" height="35" src="{{'/images/wiki.png'|media}}" @@ -92,12 +128,14 @@ poor design of the data or methods on data objects #} {% trans %}posted{% endtrans %} {% endif %} {% if post.__class__.__name__ == 'PostRevision' %} - <strong>{{post.revised_at|diff_date}}</strong> + <strong>{{ timeago(post.revised_at) }}</strong> {% else %} - <strong>{{post.added_at|diff_date}}</strong> + <strong>{{ timeago(post.added_at) }}</strong> {% endif %} </p> - {{ post_contributor_avatar_and_credentials(post, post.author) }} + {{ post_contributor_avatar_and_credentials( + post, post.author, karma_mode = karma_mode, badges_mode = badges_mode + ) }} {% endif %} </div> {% elif contributor_type=="last_updater" %} @@ -119,10 +157,15 @@ poor design of the data or methods on data objects #} {% else %} href="{% url answer_revisions post.id %}" {% endif %} - >{% trans %}updated{% endtrans %} <strong>{{ last_edited_at|diff_date }}</strong></a> + >{% trans %}updated{% endtrans %} <strong>{{ timeago(last_edited_at) }}</strong></a> </p> {% if original_author != update_author or is_wiki %} - {{ post_contributor_avatar_and_credentials(post, update_author) }} + {{ + post_contributor_avatar_and_credentials( + post, update_author, + karma_mode = karma_mode, badges_mode = badges_mode + ) + }} {% endif %} </div> {% endif %} @@ -178,6 +221,24 @@ poor design of the data or methods on data objects #} </ul> {%- endmacro -%} +{%- macro user_group(group) -%} + {% if group.group_profile.logo_url %} + <a href="{% url users_by_group group.id, group.name|replace('-', ' ')|slugify %}"> + <img class="group-logo" src="{{group.group_profile.logo_url}}" alt='{% trans name=group.name|escape %}logo for user group "{{name}}"{% endtrans %}' /> + </a> + {% endif %} + <div class="group-name"> + <a + href="{% url users_by_group group.id, group.name|replace('-', ' ')|slugify %}" + >{{ group.name|escape }}</a> + </div> + <!--div id="group-{{group.id}}-description"> + {% if group.tag_wiki %} + {{ group.tag_wiki.html }} + {% endif %} + </div--> +{%- endmacro -%} + {# todo: remove the extra content argument to make its usage more explicit #} {%- macro tag_widget( tag, @@ -306,7 +367,7 @@ for the purposes of the AJAX comment editor #} <div class="comment-body"> {{comment.html}} <a class="author" href="{{comment.author.get_profile_url()}}">{{comment.author.username}}</a> - <span class="age"> ({{comment.added_at|diff_date}})</span> + <span class="age"> ({{ timeago(comment.added_at) }})</span> <a id="post-{{comment.id}}-edit" class="edit">{% trans %}edit{% endtrans %}</a> </div> @@ -422,7 +483,11 @@ for the purposes of the AJAX comment editor #} answer {% if answer.accepted() %}accepted-answer{% endif %} {% if answer.author_id==question.author_id %} answered-by-owner{% endif %} {% if answer.deleted %}deleted{% endif -%} {%- endmacro -%} -{%- macro user_score_and_badge_summary(user) -%} +{%- macro user_score_and_badge_summary( + user, + karma_mode = None, + badges_mode = None +) -%} {%include "widgets/user_score_and_badge_summary.html"%} {%- endmacro -%} @@ -457,8 +522,8 @@ answer {% if answer.accepted() %}accepted-answer{% endif %} {% if answer.author_ {% endif %} {%- endmacro -%} -{%- macro user_long_score_and_badge_summary(user) -%} - {% include "widgets/user_long_score_and_badge_summary.html" %} +{%- macro user_long_score_and_badge_summary(user, badges_mode = None) -%} + {%- include "widgets/user_long_score_and_badge_summary.html" -%} {%- endmacro -%} {%- macro user_country_flag(user) -%} @@ -491,7 +556,10 @@ answer {% if answer.accepted() %}accepted-answer{% endif %} {% if answer.author_ {{ user_country_name_and_flag(user) }} {%- endmacro -%} -{%- macro user_list(users, profile_section = None) -%} +{%- macro user_list( + users, profile_section = None, karma_mode = None, badges_mode = None + ) +-%} {% include "widgets/user_list.html"%} {%- endmacro -%} @@ -641,3 +709,9 @@ answer {% if answer.accepted() %}accepted-answer{% endif %} {% if answer.author_ </a> {% endif %} {%- endmacro -%} + +{%- macro timeago(datetime_object) -%} + <abbr class="timeago" title="{{datetime_object.replace(microsecond=0)|add_tz_offset}}"> + {{datetime_object.replace(microsecond=0)|add_tz_offset}} + </abbr> +{%- endmacro -%} diff --git a/askbot/skins/default/templates/main_page/headline.html b/askbot/skins/default/templates/main_page/headline.html index 19dc7063..cc6f47a5 100644 --- a/askbot/skins/default/templates/main_page/headline.html +++ b/askbot/skins/default/templates/main_page/headline.html @@ -20,7 +20,7 @@ }} </div> {% endif %} - {% if author_name or search_tags or query %} + {#% if author_name or search_tags or query %} <p class="search-tips"><b>{% trans %}Search tips:{% endtrans %}</b> {% if reset_method_count > 1 %} {% if author_name %} @@ -39,5 +39,5 @@ </p> {% else %} <p class="search-tips"><b>{% trans %}Search tip:{% endtrans %}</b> {% trans %}add tags and a query to focus your search{% endtrans %}</p> - {% endif %} + {% endif %#} {% endif %} diff --git a/askbot/skins/default/templates/main_page/javascript.html b/askbot/skins/default/templates/main_page/javascript.html index 6a90c758..d968dcd5 100644 --- a/askbot/skins/default/templates/main_page/javascript.html +++ b/askbot/skins/default/templates/main_page/javascript.html @@ -17,6 +17,7 @@ askbot['urls']['mark_interesting_tag'] = '{% url mark_interesting_tag %}'; askbot['urls']['mark_ignored_tag'] = '{% url mark_ignored_tag %}'; + 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['urls']['questions'] = '{% url "questions" %}'; diff --git a/askbot/skins/default/templates/main_page/sidebar.html b/askbot/skins/default/templates/main_page/sidebar.html index 9fb8fab9..7acbe091 100644 --- a/askbot/skins/default/templates/main_page/sidebar.html +++ b/askbot/skins/default/templates/main_page/sidebar.html @@ -1,13 +1,19 @@ {% import "macros.html" as macros %} +{% if settings.SIDEBAR_MAIN_HEADER %} <div class="box"> {{ settings.SIDEBAR_MAIN_HEADER }} </div> +{% endif %} {% if contributors and settings.SIDEBAR_MAIN_SHOW_AVATARS %} {% include "widgets/contributors.html" %} {% endif %} +{% if settings.TAG_SEARCH_INPUT_ENABLED %} + {% include "main_page/tag_search.html" %} +{% endif %} + {% if request.user.is_authenticated() and settings.SIDEBAR_MAIN_SHOW_TAG_SELECTOR %} {% include "widgets/tag_selector.html" %} {% endif %} @@ -16,6 +22,8 @@ {% include "widgets/related_tags.html" %} {% endif %} +{% if settings.SIDEBARE_MAIN_FOOTER %} <div class="box"> {{ settings.SIDEBAR_MAIN_FOOTER }} </div> +{% endif %} diff --git a/askbot/skins/default/templates/main_page/tag_search.html b/askbot/skins/default/templates/main_page/tag_search.html new file mode 100644 index 00000000..45f12b2f --- /dev/null +++ b/askbot/skins/default/templates/main_page/tag_search.html @@ -0,0 +1,7 @@ +<div id="tagSearch" class="box"> + <h2>{% trans %}Tag search{% endtrans %}</h2> + <div class="inputs"> + <input id="ab-tag-search" autocomplete="off" type="text"/> + <input id="ab-tag-search-add" type="submit" value="{% trans %}search{% endtrans %}"/> + </div> +</div> diff --git a/askbot/skins/default/templates/meta/bottom_scripts.html b/askbot/skins/default/templates/meta/bottom_scripts.html index d77145e3..2603ec31 100644 --- a/askbot/skins/default/templates/meta/bottom_scripts.html +++ b/askbot/skins/default/templates/meta/bottom_scripts.html @@ -30,8 +30,8 @@ ></script> <!-- History.js --> <script type='text/javascript' src="{{"/js/jquery.history.js"|media }}"></script> -<script type='text/javascript' src="{{"/js/utils.js"|media }}"></script> <script type="text/javascript" src="{% url django.views.i18n.javascript_catalog %}"></script> +<script type='text/javascript' src="{{"/js/utils.js"|media }}"></script> {% if settings.ENABLE_MATHJAX %} <script type='text/javascript' src="{{settings.MATHJAX_BASE_URL}}/MathJax.js"> MathJax.Hub.Config({ @@ -73,6 +73,7 @@ $('#validate_email_alert').click(function(){notify.close(true)}) notify.show(); {% endif %} + $('abbr.timeago').timeago(); </script> {% if settings.USE_CUSTOM_JS %} <script diff --git a/askbot/skins/default/templates/question/sidebar.html b/askbot/skins/default/templates/question/sidebar.html index 86a543c7..620268e1 100644 --- a/askbot/skins/default/templates/question/sidebar.html +++ b/askbot/skins/default/templates/question/sidebar.html @@ -1,4 +1,4 @@ -{% import "macros.html" as macros %} +{% from "macros.html" import timeago %} <div class="box"> {{ settings.SIDEBAR_QUESTION_HEADER }} </div> @@ -45,7 +45,7 @@ <div class="clearfix"></div> <h2>{% trans %}Stats{% endtrans %}</h2> <p> - {% trans %}Asked{% endtrans %}: <strong title="{{ question.added_at }}">{{question.added_at|diff_date}}</strong> + {% trans %}Asked{% endtrans %}: {{ timeago(question.added_at) }} </p> <p> {% trans %}Seen{% endtrans %}: <strong>{{ thread.view_count|intcomma }} {% trans %}times{% endtrans %}</strong> diff --git a/askbot/skins/default/templates/reopen.html b/askbot/skins/default/templates/reopen.html index d1ccc313..894fa3a0 100644 --- a/askbot/skins/default/templates/reopen.html +++ b/askbot/skins/default/templates/reopen.html @@ -1,4 +1,5 @@ {% extends "two_column_body.html" %} +{% from "macros.html" import timeago %} <!-- reopen.html --> {% block title %}{% spaceless %}{% trans %}Reopen question{% endtrans %}{% endspaceless %}{% endblock %} {% block content %} @@ -16,7 +17,7 @@ {% trans %}Close reason:{% endtrans %} "<strong>{{question.thread.get_close_reason_display()}}</strong>". </p> <p> - {% trans %}When:{% endtrans %} {{question.thread.closed_at|diff_date}} + {% trans %}When:{% endtrans %} {{ timeago(question.thread.closed_at) }} </p> <p> {% trans %}Reopen this question?{% endtrans %} diff --git a/askbot/skins/default/templates/user_profile/reject_post_dialog.html b/askbot/skins/default/templates/user_profile/reject_post_dialog.html new file mode 100644 index 00000000..987c511a --- /dev/null +++ b/askbot/skins/default/templates/user_profile/reject_post_dialog.html @@ -0,0 +1,108 @@ +<div class="modal" style="display:none" id="reject-edit-modal"> + <div class="modal-header"> + <a class="close" data-dismiss="modal">x</a> + <h3>{% trans %}Reject the post(s)?{% endtrans %}</h3> + </div> + <div id="reject-edit-modal-add-new">{# create new reject reason #} + <div class="modal-body"> + <input + class="reject-reason-title tipped-input blank" + type="text" + value="{% trans %}1) Enter a brief description of why you are rejecting the post.{% endtrans %}" + /> + <textarea class="reject-reason-details tipped-input blank" + >{% trans %}2) Please enter details here. This text will be sent to the user.{% endtrans %}</textarea> + </div> + <div class="modal-footer"> + <div class="btn-toolbar"> + <div class="btn-group dropup"> + <button class="btn btn-danger save-reason-and-reject" + >{% trans %}Use this reason & reject{% endtrans %}</button> + <button class="btn btn-danger dropdown-toggle" data-toggle="dropdown"> + <span class="caret"></span> + </button> + <ul class="dropdown-menu"> + <li> + <a class="select-other-reason" href="#" + >{% trans %}Use other reason{% endtrans %}</a> + </li> + </ul> + </div> + <div class="btn-group"> + <a class="btn save-reason" + >{% trans %}Save reason, but do not reject{% endtrans %}</a> + </div> + <div class="btn-group"> + <a class="btn cancel">{% trans %}Cancel{% endtrans %}</a> + </div> + </div> + </div> + </div> + <div id="reject-edit-modal-select">{# select one of existing reasons #} + <div class="modal-body"> + <p>{% trans %}Please, choose a reason for the rejection.{% endtrans %}</p> + <ul class="select-box"> + {% for reason in post_reject_reasons %} + <li + data-original-title="{{reason.details.text|escape}}" + data-item-id="{{reason.id}}" + >{{reason.title|escape}}</li> + {% endfor %} + </ul> + </div> + <div class="modal-footer"> + <div class="btn-toolbar"> + <div class="btn-group dropup"> + <a class="btn select-this-reason" + >{% trans %}Select this reason{% endtrans %}</a> + <a class="btn dropdown-toggle" data-toggle="dropdown"> + <span class="caret"></span> + </a> + <ul class="dropdown-menu"> + <li> + <a class="delete-this-reason" + >{% trans %}Delete this reason{% endtrans %}</a> + </li> + </ul> + </div> + <div class="btn-group"> + <a class="btn add-new-reason" + >{% trans %}Add a new reason{% endtrans %}</a> + </div> + <div class="btn-group"> + <a class="btn cancel">{% trans %}Cancel{% endtrans %}</a> + </div> + </div> + </div> + </div> + <div id="reject-edit-modal-preview">{# preview reject reason #} + <div class="modal-body"> + <p>{% trans %}You have selected reason for the rejection <strong>"<span class="selected-reason-title"></span>"</strong>. The text below will be sent to the user and the post(s) will be deleted:{% endtrans %}</p> + <textarea disabled="disabled" class="selected-reason-details"></textarea> + </div> + <div class="modal-footer"> + <div class="btn-toolbar"> + <div class="btn-group dropup"> + <a class="btn btn-danger reject" + >{% trans %}Use this reason & reject{% endtrans %}</a> + <a class="btn btn-danger dropdown-toggle" data-toggle="dropdown"> + <span class="caret"></span> + </a> + <ul class="dropdown-menu"> + <li> + <a class="select-other-reason" + >{% trans %}Use other reason{% endtrans %}</a> + </li> + </ul> + </div> + <div class="btn-group"> + <a class="btn edit-reason" + >{% trans %}Edit this reason{% endtrans %}</a> + </div> + <div class="btn-group"> + <a class="btn cancel">{% trans %}Cancel{% endtrans %}</a> + </div> + </div> + </div> + </div> +</div> diff --git a/askbot/skins/default/templates/user_profile/user.html b/askbot/skins/default/templates/user_profile/user.html index 789c3c86..15e0622a 100644 --- a/askbot/skins/default/templates/user_profile/user.html +++ b/askbot/skins/default/templates/user_profile/user.html @@ -22,6 +22,9 @@ <script type="text/javascript"> var viewUserID = {{view_user.id}}; askbot['data']['viewUserName'] = '{{ view_user.username }}'; + askbot['data']['viewUserId'] = {{view_user.id}}; + askbot['urls']['edit_group_membership'] = '{% url edit_group_membership %}'; + askbot['urls']['get_groups_list'] = '{% url get_groups_list %}'; </script> {% if request.user|can_moderate_user(view_user) %} <script type='text/javascript' src='{{"/js/jquery.form.js"|media}}'></script> diff --git a/askbot/skins/default/templates/user_profile/user_inbox.html b/askbot/skins/default/templates/user_profile/user_inbox.html index f70f1884..cda45027 100644 --- a/askbot/skins/default/templates/user_profile/user_inbox.html +++ b/askbot/skins/default/templates/user_profile/user_inbox.html @@ -1,5 +1,8 @@ {% extends "user_profile/user.html" %} {% import "macros.html" as macros %} +{% block before_css %} + <link href="{{'/bootstrap/css/bootstrap.css'|media}}" rel="stylesheet" type="text/css" /> +{% endblock %} <!-- user_responses.html --> {# This template accepts a list of response list @@ -44,79 +47,38 @@ inbox_section - forum|flags </a> </div> {% endif %} - {% if inbox_section == 'forum' %} - <div id="re_tools"> - <strong>{% trans %}select:{% endtrans %}</strong> - <a id="sel_all">{% trans %}all{% endtrans %}</a> | - <a id="sel_seen">{% trans %}seen{% endtrans %}</a> | - <a id="sel_new">{% trans %}new{% endtrans %}</a> | - <a id="sel_none">{% trans %}none{% endtrans %}</a><br /> - <button id="re_mark_seen">{% trans %}mark as seen{% endtrans %}</button> - <button id="re_mark_new">{% trans %}mark as new{% endtrans %}</button> - <button id="re_dismiss">{% trans %}dismiss{% endtrans %}</button> - </div> - {% endif %} - {% if inbox_section == 'flags' %} <div id="re_tools"> <strong>{% trans %}select:{% endtrans %}</strong> <a id="sel_all">{% trans %}all{% endtrans %}</a> | <a id="sel_seen">{% trans %}seen{% endtrans %}</a> | <a id="sel_new">{% trans %}new{% endtrans %}</a> | <a id="sel_none">{% trans %}none{% endtrans %}</a><br /> - <button id="re_remove_flag">{% trans %}remove flags{% endtrans %}</button> - <button id="re_close">{% trans %}close{% endtrans %}</button> - <button id="re_delete_post">{% trans %}delete post{% endtrans %}</button> + <div class="btn-group"> + {% if inbox_section == 'forum' %} + <a class="btn" id="re_mark_seen">{% trans %}mark as seen{% endtrans %}</a> + <a class="btn" id="re_mark_new">{% trans %}mark as new{% endtrans %}</a> + <a class="btn" id="re_dismiss">{% trans %}dismiss{% endtrans %}</a> + {% else %} + <a class="btn" id="re_remove_flag">{% trans %}remove flags/approve{% endtrans %}</a> + <a + class="btn" + id="re_delete_post" + >{% trans %}delete post{% endtrans %}</a> + {% endif %} + </div> </div> - {% endif %} + {% include "user_profile/reject_post_dialog.html" %} <div id="responses"> {% for response in responses %} - <div class="response-parent"> - <p class="headline"> + <div class="response-parent"> + <p class="headline"> <strong>"{{ response.response_title.strip()|escape}}"</strong> - </p> - <div id="re_{{response.id}}" class="re{% if response.is_new %} new highlight{% else %} seen{% endif %}"> - <input type="checkbox" /> - <div class="face"> - {{ macros.gravatar(response.user, 48) }} - </div> - <a style="font-size:12px" href="{{ response.user.get_absolute_url() }}">{{ response.user.username }}</a> - <a style="text-decoration:none;" href="{{ response.response_url }}"> - {{ response.response_type }} - ({{ response.timestamp|diff_date(True) }}):<br/> - {% if inbox_section != 'flags' %} - {{ response.response_snippet }} - {% endif %} - </a> - {% if inbox_section == 'flags' %} - <a class="re_expand" href="{{ response.response_url }}"> - <div class="re_snippet">{{ response.response_snippet }}</div> - <div class="re_content">{{ response.response_content }}</div></a> - {% endif %} - </div> - {% if response.nested_responses %} - {%for nested_response in response.nested_responses %} - <div id="re_{{nested_response.id}}" class="re{% if nested_response.is_new %} new highlight{% else %} seen{% endif %}"> - <input type="checkbox" /> - <div class="face"> - {{ macros.gravatar(nested_response.user, 48) }} - </div> - <a style="font-size:12px" href="{{ nested_response.user.get_absolute_url() }}">{{ nested_response.user.username }}</a> - <a style="text-decoration:none;" href="{{ nested_response.response_url }}"> - {{ nested_response.response_type }} - ({{ nested_response.timestamp|diff_date(True) }}):<br/> - {% if inbox_section != 'flags' %} - {{ nested_response.response_snippet }} - {% endif %} - </a> - {% if inbox_section == 'flags' %} - <a class="re_expand" href="{{ nested_response.response_url }}"> - <div class="re_snippet">{{ nested_response.response_snippet }}</div> - <div class="re_content">{{ nested_response.response_content }}</div></a> - {% endif %} - </div> - {%endfor%} - {%endif%} - </div> + </p> + {{ macros.inbox_post_snippet(response, inbox_section) }} + {% for nested_response in response.nested_responses %} + {{ macros.inbox_post_snippet(nested_response, inbox_section) }} + {%endfor%} + </div> {% endfor %} </div> </div> @@ -126,6 +88,12 @@ inbox_section - forum|flags var askbot = askbot || {}; askbot['urls'] = askbot['urls'] || {}; askbot['urls']['manageInbox'] = '{% url manage_inbox %}'; + askbot['urls']['save_post_reject_reason'] = '{% url save_post_reject_reason %}'; + askbot['urls']['delete_post_reject_reason'] = '{% url delete_post_reject_reason %}'; + $(document).ready(function(){ + setup_inbox(); + }); </script> + <script type="text/javascript" src="{{'/bootstrap/js/bootstrap.js'|media}}" /> <!-- end user_responses.html --> {% endblock %} diff --git a/askbot/skins/default/templates/user_profile/user_info.html b/askbot/skins/default/templates/user_profile/user_info.html index 6d286c0a..18e74464 100644 --- a/askbot/skins/default/templates/user_profile/user_info.html +++ b/askbot/skins/default/templates/user_profile/user_info.html @@ -21,8 +21,10 @@ {% endif %} {% endif %} </div> - <div class="scoreNumber">{{view_user.reputation|intcomma}}</div> - <p><b style="color:#777;">{% trans %}karma{% endtrans %}</b></p> + {% if can_show_karma %} + <div class="scoreNumber">{{view_user.reputation|intcomma}}</div> + <p><b style="color:#777;">{% trans %}karma{% endtrans %}</b></p> + {% endif %} {% if user_follow_feature_on %} {{ macros.follow_user_toggle(visitor = request.user, subject = view_user) }} {% endif %} @@ -56,12 +58,12 @@ {% endif %} <tr> <td>{% trans %}member since{% endtrans %}</td> - <td><strong>{{ view_user.date_joined|diff_date }}</strong></td> + <td><strong>{{ macros.timeago(view_user.date_joined) }}</strong></td> </tr> {% if view_user.last_seen %} <tr> <td>{% trans %}last seen{% endtrans %}</td> - <td><strong title="{{ view_user.last_seen }}">{{view_user.last_seen|diff_date}}</strong></td> + <td><strong title="{{ view_user.last_seen }}">{{ macros.timeago(view_user.last_seen) }}</strong></td> </tr> {% endif %} {% if view_user.website %} diff --git a/askbot/skins/default/templates/user_profile/user_network.html b/askbot/skins/default/templates/user_profile/user_network.html index 1fd2e06a..e6134e0c 100644 --- a/askbot/skins/default/templates/user_profile/user_network.html +++ b/askbot/skins/default/templates/user_profile/user_network.html @@ -8,11 +8,25 @@ {% if followed_users or followers %} {% if followers %} <h2>{% trans count=followers|length %}Followed by {{count}} person{% pluralize count %}Followed by {{count}} people{% endtrans %}</h2> - {{ macros.user_list(followers, profile_section = 'network') }} + {{ + macros.user_list( + followers, + profile_section = 'network', + karma_mode = settings.KARMA_MODE, + badges_mode = settings.BADGES_MODE + ) + }} {% endif %} {% if followed_users %} <h2>{% trans count=followed_users|length %}Following {{count}} person{% pluralize count %}Following {{count}} people{% endtrans %}</h2> - {{ macros.user_list(followed_users, profile_section = 'network') }} + {{ + macros.user_list( + followed_users, + profile_section = 'network', + karma_mode = settings.KARMA_MODE, + badges_mode = settings.BADGES_MODE + ) + }} {% endif %} {% else %} {% if request.user == view_user %} diff --git a/askbot/skins/default/templates/user_profile/user_recent.html b/askbot/skins/default/templates/user_profile/user_recent.html index bace94d8..8eae673d 100644 --- a/askbot/skins/default/templates/user_profile/user_recent.html +++ b/askbot/skins/default/templates/user_profile/user_recent.html @@ -1,4 +1,5 @@ {% extends "user_profile/user.html" %} +{% from "macros.html" import timeago %} <!-- user_recent.html --> {% block profilesection %} {% trans %}activity{% endtrans %} @@ -7,7 +8,7 @@ <div style="padding-top:5px;font-size:13px;"> {% for act in activities %} <div style="clear:both;line-height:20px" > - <div style="width:180px;float:left">{{ act.time|diff_date(True) }}</div> + <div style="width:180px;float:left">{{ timeago(act.time) }}</div> <div style="width:150px;float:left"> <span class="user-action-{{ act.type_id }}">{{ act.type }}</span> </div> diff --git a/askbot/skins/default/templates/user_profile/user_reputation.html b/askbot/skins/default/templates/user_profile/user_reputation.html index 0deb2b97..1bb9b1ba 100644 --- a/askbot/skins/default/templates/user_profile/user_reputation.html +++ b/askbot/skins/default/templates/user_profile/user_reputation.html @@ -1,4 +1,5 @@ {% extends "user_profile/user.html" %} +{% from "macros.html" import timeago %} <!-- user_reputation.html --> {% block profilesection %} {% trans %}karma{% endtrans %} @@ -17,7 +18,7 @@ <span class="karma-gained">{{ rep.positive }}</span> <span class="karma-lost">{{ rep.negative }}</span> {{ rep.get_explanation_snippet() }} - <span class="small">({{rep.reputed_at|diff_date}})</span> + <span class="small">({{ timeago(rep.reputed_at) }})</span> <div class="clean"></div> </p> {% endfor %} diff --git a/askbot/skins/default/templates/user_profile/user_stats.html b/askbot/skins/default/templates/user_profile/user_stats.html index 774550d8..43b7f4fa 100644 --- a/askbot/skins/default/templates/user_profile/user_stats.html +++ b/askbot/skins/default/templates/user_profile/user_stats.html @@ -6,6 +6,25 @@ {% endblock %} {% block usercontent %} {% include "user_profile/user_info.html" %} + {% if settings.GROUPS_ENABLED %} + <div id="user-groups"> + <h2>{% trans + username = view_user.username + %}{{username}}'s groups{% endtrans %} + </h2> + <ul id="groups-list"> + {% if user_groups %} + {% for group in user_groups %} + <li> + {{ macros.user_group(group) }} + </li> + {% endfor %} + {% endif %} + </ul> + <div class="clearfix"></div> + <a id="add-group">{% trans %}add group{% endtrans %}</a> + </div> + {% endif %} <a name="questions"></a> {% spaceless %} <h2>{% trans counter=question_count %}<span class="count">{{counter}}</span> Question{% pluralize %}<span class="count">{{counter}}</span> Questions{% endtrans %}</h2> @@ -92,6 +111,7 @@ </tr> </table> </div> + {% if settings.BADGES_MODE == 'public' %} <a name="badges"></a> {% spaceless %} <h2>{% trans counter=total_badges %}<span class="count">{{counter}}</span> Badge{% pluralize %}<span class="count">{{counter}}</span> Badges{% endtrans %}</h2> @@ -131,26 +151,13 @@ </tr> </table> </div> + {% endif %} {% endblock %} {% block endjs %} {{ super() }} <script type="text/javascript"> $(document).ready(function(){ - $('.badge-context-toggle').each(function(idx, elem){ - var context_list = $(elem).parent().next('ul'); - if (context_list.children().length > 0){ - $(elem).addClass('active'); - var toggle_display = function(){ - if (context_list.css('display') == 'none'){ - $('.badge-context-list').hide();{# hide all context lists #} - context_list.show(); - } else { - context_list.hide(); - } - }; - $(elem).click(toggle_display); - } - }); + setup_badge_details_toggle(); }); </script> {% endblock %} diff --git a/askbot/skins/default/templates/user_profile/user_tabs.html b/askbot/skins/default/templates/user_profile/user_tabs.html index e6aead31..b8c56479 100644 --- a/askbot/skins/default/templates/user_profile/user_tabs.html +++ b/askbot/skins/default/templates/user_profile/user_tabs.html @@ -17,10 +17,12 @@ href="{% url user_profile view_user.id, view_user.username|slugify %}?sort=network" ><span>{% trans %}network{% endtrans %}</span></a> {% endif %} + {% if can_show_karma %} <a id="reputation" {% if tab_name=="reputation" %}class="on"{% endif %} title="{% trans %}Graph of user karma{% endtrans %}" href="{% url user_profile view_user.id, view_user.username|slugify %}?sort=reputation" ><span>{% trans %}karma{% endtrans %}</span></a> + {% endif %} <a id="favorites" {% if tab_name=="favorites" %}class="on"{% endif %} title="{% trans %}questions that user is following{% endtrans %}" href="{% url user_profile view_user.id, view_user.username|slugify %}?sort=favorites" diff --git a/askbot/skins/default/templates/user_profile/user_votes.html b/askbot/skins/default/templates/user_profile/user_votes.html index 5111a580..b5fc4560 100644 --- a/askbot/skins/default/templates/user_profile/user_votes.html +++ b/askbot/skins/default/templates/user_profile/user_votes.html @@ -1,4 +1,5 @@ {% extends "user_profile/user.html" %} +{% from "macros.html" import timeago %} <!-- user_votes.html --> {% block profilesection %} {% trans %}votes{% endtrans %} @@ -7,7 +8,7 @@ <div style="padding-top:5px;font-size:13px;"> {% for vote in votes %} <div style="clear:both;line-height:20px" > - <div style="width:150px;float:left">{{vote.voted_at|diff_date(True)}}</div> + <div style="width:150px;float:left">{{ timeago(vote.voted_at) }}</div> <div style="width:30px;float:left"> {% if vote.vote==1 %} <img src="{{"/images/vote-arrow-up-on-new.png"|media}}" title="{% trans %}upvote{% endtrans %}"> diff --git a/askbot/skins/default/templates/users.html b/askbot/skins/default/templates/users.html index f2225772..3fa35643 100644 --- a/askbot/skins/default/templates/users.html +++ b/askbot/skins/default/templates/users.html @@ -2,55 +2,134 @@ {% import "macros.html" as macros %} <!-- users.html --> {% block title %}{% spaceless %}{% trans %}Users{% endtrans %}{% endspaceless %}{% endblock %} +{% block before_css %} + {% if group and request.user.is_authenticated() and request.user.is_administrator() %} + <link href="{{'/bootstrap/css/bootstrap.css'|media}}" rel="stylesheet" type="text/css" /> + {% endif %} +{% endblock %} +{% block forestyle %} + <link rel="stylesheet" type="text/css" href="{{"/js/wmd/wmd.css"|media}}" /> +{% endblock %} {% block content %} -<h1 class="section-title">{% trans %}Users{% endtrans %}</h1> -<div class="tabBar tabBar-user"> +<h1 class="section-title"> +{% if group %} + {% trans name = group.name|replace('-', ' ')|escape %}Users in group {{name}}{% endtrans %} +{% else %} + {% trans %}Users{% endtrans %} +{% endif %} +</h1> +<div class="tabBar"> <div class="tabsA"> <span class="label">{% trans %}Sort by »{% endtrans %}</span> + {% if settings.KARMA_MODE == 'public' %} <a id="sort_reputation" - href="{% url users %}?sort=reputation" + href="{{ request.path }}?sort=reputation" {% if tab_id == 'reputation' %}class="on"{% endif %} title="{% trans %}see people with the highest reputation{% endtrans %}" ><span>{% trans %}karma{% endtrans %}</span></a> + {% endif %} <a id="sort_newest" - href="{% url users %}?sort=newest" + href="{{ request.path }}?sort=newest" {% if tab_id == 'newest' %}class="on"{% endif %} class="off" title="{% trans %}see people who joined most recently{% endtrans %}" ><span>{% trans %}recent{% endtrans %}</span></a> <a id="sort_last" - href="{% url users %}?sort=last" + href="{{ request.path }}?sort=last" {% if tab_id == 'last' %}class="on"{% endif %} class="off" title="{% trans %}see people who joined the site first{% endtrans %}" ><span>{% trans %}oldest{% endtrans %}<span></a> <a id="sort_user" - href="{% url users %}?sort=user" + href="{{ request.path }}?sort=user" {% if tab_id == 'user' %}class="on"{% endif %} title="{% trans %}see people sorted by name{% endtrans %}" ><span>{% trans %}by username{% endtrans %}</span></a> </div> </div> <div class="clean"></div> -<p> - {% if suser %} - {% trans %}users matching query {{suser}}:{% endtrans %} - {% endif %} - {% if not users.object_list %} - <span>{% trans %}Nothing found.{% endtrans %}</span> - {% endif %} -</p> -{{ macros.user_list(users.object_list) }} +{% if suser %} + <p>{% trans %}users matching query {{suser}}:{% endtrans %}</p> +{% endif %} +{% if not users.object_list %} + <p><span>{% trans %}Nothing found.{% endtrans %}</span></p> +{% endif %} +{{ macros.user_list( + users.object_list, + karma_mode = settings.KARMA_MODE, badges_mode = settings.BADGES_MODE + ) +}} <div class="pager"> {{ macros.paginator(paginator_context) }} </div> {% endblock %} +{% block sidebar %} + {% if group %} + {# this widget takes variables: group, user_can_join_group, user_is_group_member #} + {% include "widgets/group_info.html" %} + {% endif %} +{% endblock %} {% block endjs %} + <script type='text/javascript'> + var Attacklab = Attacklab || {}; + Attacklab.wmd = 1;{# a trick to launch wmd manually #} + askbot['urls']['upload'] = '{% url upload %}'; + askbot['urls']['load_tag_wiki_text'] = '{% url load_tag_wiki_text %}'; + askbot['urls']['save_tag_wiki_text'] = '{% url save_tag_wiki_text %}'; + askbot['urls']['save_group_logo_url'] = '{% url save_group_logo_url %}'; + askbot['urls']['delete_group_logo_url'] = '{% url delete_group_logo %}'; + askbot['urls']['join_or_leave_group'] = '{% url join_or_leave_group %}'; + </script> + <script type="text/javascript" src='{{"/bootstrap/js/bootstrap.js"|media}}'></script> + <script type='text/javascript' src='{{"/js/editor.js"|media}}'></script> + <script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script> + <script type='text/javascript' src='{{"/js/wmd/wmd.js"|media}}'></script> + <script type='text/javascript' src='{{"/js/jquery.validate.min.js"|media}}'></script> + <script src='{{"/js/post.js"|media}}' type='text/javascript'></script> <script type="text/javascript"> //todo move javascript out + {% if settings.ENABLE_MATHJAX or settings.MARKUP_CODE_FRIENDLY %} + var codeFriendlyMarkdown = true; + {% else %} + var codeFriendlyMarkdown = false; + {% endif %} $().ready(function(){ + {% if group and request.user.is_authenticated() %} + var group_join_btn = new GroupJoinButton({{ group.id }}); + group_join_btn.decorate($('.group-join-btn')); + {% endif %} + //setup WMD editor + if (askbot['data']['userIsAdminOrMod'] === true){ + //todo: this is kind of Attacklab.init ... should not be here + Attacklab.wmd = function(){ + Attacklab.loadEnv = function(){ + var mergeEnv = function(env){ + if(!env){ + return; + } + + for(var key in env){ + Attacklab.wmd_env[key] = env[key]; + } + }; + + mergeEnv(Attacklab.wmd_defaults); + mergeEnv(Attacklab.account_options); + mergeEnv(top["wmd_options"]); + Attacklab.full = true; + + var defaultButtons = "bold italic link blockquote code image ol ul heading hr"; + Attacklab.wmd_env.buttons = Attacklab.wmd_env.buttons || defaultButtons; + }; + Attacklab.loadEnv(); + }; + Attacklab.wmd(); + Attacklab.wmdBase(); + var group_editor = new UserGroupProfileEditor(); + group_editor.decorate($('#group-wiki-{{group.id}}')); + } Hilite.exact = false; Hilite.elementid = "main-body"; Hilite.debug_referrer = location.href; diff --git a/askbot/skins/default/templates/widgets/group_info.html b/askbot/skins/default/templates/widgets/group_info.html new file mode 100644 index 00000000..601930af --- /dev/null +++ b/askbot/skins/default/templates/widgets/group_info.html @@ -0,0 +1,92 @@ +<div id="group-wiki-{{group.id}}" class="box group-wiki"> + <h2>{% trans %}Group info{% endtrans %}</h2> + <img class="group-logo" + {% if group.group_profile.logo_url %} + src="{{ group.group_profile.logo_url }}" + {% else %} + style="display:none" + {% endif %} + /> + <div class="content"> + {% if group.tag_wiki %} + {{ group.tag_wiki.html }} + {% endif %} + </div> + <div class="clearfix"></div> + {% if user_can_join_group or user_is_group_member %} + <button + class="group-join-btn follow-toggle {% if user_is_group_member %}on on-state{% endif %}" + data-off-prompt-text="{% trans %}Leave this group{% endtrans %}" + data-on-prompt-text="{% trans %}Join this group{% endtrans %}" + data-on-state-text="{% trans %}You are a member{% endtrans %}" + data-off-state-text="{% trans %}Join this group{% endtrans %}" + > + {% if user_is_group_member %} + {% trans %}You are a member{% endtrans %} + {% else %} + {% if user_can_join_group %} + {% trans %}Join this group{% endtrans %} + {% endif %} + {% endif %} + </button> + {% endif %} + {% if request.user.is_authenticated() and request.user.is_administrator() %} + <div class="controls"> + <a class="edit-description" + >{% trans %}edit description{% endtrans %}</a> + {% if group.group_profile.logo_url %} + <span>|</span> + <a class="change-logo" + >{% trans %}change logo{% endtrans %}</a> + <span>|</span> + <a class="delete-logo">{% trans %}delete logo{% endtrans %}</a> + {% else %} + <span>|</span> + <a class="change-logo" + >{% trans %}add logo{% endtrans %}</a> + {% endif %} + <br/> + {% if group_email_moderation_enabled %} + <input type="checkbox" + id="moderate-email" + {% if group.group_profile.moderate_email %}checked="checked"{% endif %} + data-toggle-url="{% url toggle_group_profile_property %}" + /> + <label for="moderate-email"> + {% trans %}moderate emailed questions{% endtrans %} + </label> + <br/> + {% endif %} + <input type="checkbox" + id="open-or-close-group" + {% if group.group_profile.is_open %}checked="checked"{% endif %} + data-toggle-url="{% url toggle_group_profile_property %}" + /> + <label for="open-or-close-group"> + {% trans %}anyone can join{% endtrans %} + </label> + <br/> + <a + id="preapproved-emails" + title="{% trans %}list of email addresses of pre-approved users{% endtrans %}" + data-object-id="{{group.group_profile.id}}" + data-model-name="GroupProfile" + data-property-name="preapproved_emails" + data-url="{% url edit_object_property_text %}" + data-editor-heading="{% trans %}List of preapproved email addresses{% endtrans %}" + data-help-text="{% trans %}Users with these email adderesses will be added to the group automatically.{% endtrans %}" + >{% trans %}edit preapproved emails{% endtrans %}</a> + <br/> + <a + id="preapproved-email-domains" + title="{% trans %}list of preapproved email address domain names{% endtrans %}" + data-object-id="{{group.group_profile.id}}" + data-model-name="GroupProfile" + data-property-name="preapproved_email_domains" + data-url="{% url edit_object_property_text %}" + data-editor-heading="{% trans %}List of preapproved email domain names{% endtrans %}" + data-help-text="{% trans %}Users whose email adderesses belong to these domains will be added to the group automatically.{% endtrans %}" + >{% trans %}edit preapproved email domains{% endtrans %}</a> + </div> + {% endif %} +</div> diff --git a/askbot/skins/default/templates/widgets/group_snippet.html b/askbot/skins/default/templates/widgets/group_snippet.html new file mode 100644 index 00000000..e9364a7e --- /dev/null +++ b/askbot/skins/default/templates/widgets/group_snippet.html @@ -0,0 +1,2 @@ +{% import "macros.html" as macros %} +{{ macros.user_group(group) }} diff --git a/askbot/skins/default/templates/widgets/meta_nav.html b/askbot/skins/default/templates/widgets/meta_nav.html index b459b025..1b28c787 100644 --- a/askbot/skins/default/templates/widgets/meta_nav.html +++ b/askbot/skins/default/templates/widgets/meta_nav.html @@ -8,8 +8,18 @@ href="{% url users %}" {% if active_tab == 'users' %}class="on"{% endif %} >{% trans %}users{% endtrans %}</a> +{% if settings.GROUPS_ENABLED %} +<a + id="navGroups" + href="{% url groups %}" + {% if active_tab == 'groups' %}class="on"{% endif %} +>{% trans %}groups{% endtrans %} +</a> +{% endif %} +{% if settings.BADGES_MODE == 'public' %} <a id="navBadges" href="{% url badges %}" {% if active_tab == 'badges' %}class="on"{% endif %} >{% trans %}badges{% endtrans %}</a> +{% endif %} diff --git a/askbot/skins/default/templates/widgets/question_summary.html b/askbot/skins/default/templates/widgets/question_summary.html index 56154847..c6e7bc5d 100644 --- a/askbot/skins/default/templates/widgets/question_summary.html +++ b/askbot/skins/default/templates/widgets/question_summary.html @@ -1,4 +1,4 @@ -{% from "macros.html" import user_country_flag, tag_list_widget %} +{% from "macros.html" import user_country_flag, tag_list_widget, timeago %} <div class="short-summary{% if extra_class %} {{extra_class}}{% endif %}" id="question-{{question.id}}"> <div class="counts"> <div class="views @@ -42,8 +42,7 @@ </div> <div style="clear:both"></div> <div class="userinfo"> - {# We have to kill microseconds below because InnoDB doesn't support them and all kinds of funny things happen in unit tests #} - <span class="relativetime" title="{{thread.last_activity_at.replace(microsecond=0)}}">{{ thread.last_activity_at|diff_date }}</span> + {{ timeago(thread.last_activity_at) }} {% if question.is_anonymous %} <span class="anonymous">{{ thread.last_activity_by.get_anonymous_name() }}</span> {% else %} diff --git a/askbot/skins/default/templates/widgets/user_list.html b/askbot/skins/default/templates/widgets/user_list.html index 7874946b..11f2ed50 100644 --- a/askbot/skins/default/templates/widgets/user_list.html +++ b/askbot/skins/default/templates/widgets/user_list.html @@ -8,7 +8,13 @@ <ul> <li class="thumb">{{ gravatar(user, 32) }}</li> <li><a href="{% url user_profile user.id, user.username|slugify %}{% if profile_section %}?sort={{profile_section}}{% endif %}">{{user.username}}</a>{{ user_country_flag(user) }}</li> - <li>{{ user_score_and_badge_summary(user) }}</li> + <li>{{ + user_score_and_badge_summary( + user, + karma_mode = karma_mode, + badges_mode = badges_mode + ) + }}</li> </ul> </div> {% if loop.index is divisibleby 7 %} diff --git a/askbot/skins/default/templates/widgets/user_long_score_and_badge_summary.html b/askbot/skins/default/templates/widgets/user_long_score_and_badge_summary.html index 121ae48f..efc59c55 100644 --- a/askbot/skins/default/templates/widgets/user_long_score_and_badge_summary.html +++ b/askbot/skins/default/templates/widgets/user_long_score_and_badge_summary.html @@ -1,21 +1,25 @@ +{%- if karma_mode != 'hidden' -%} <a class="user-micro-info" href="{{user.get_absolute_url()}}?sort=reputation" >{% trans %}karma:{% endtrans %} {{user.reputation}}</a> -{%- if user.gold or user.silver or user.bronze %} -<a class="user-micro-info" - href="{{user.get_absolute_url()}}#badges" -><span title="{{user.get_badge_summary}}">{% trans %}badges:{% endtrans %} - {% if user.gold %} - <span class='badge1'>●</span> - <span class="badgecount">{{user.gold}}</span> - {% endif %} - {% if user.silver %} - <span class='badge2'>●</span> - <span class="badgecount">{{user.silver}}</span> - {% endif %} - {% if user.bronze %} - <span class='badge3'>●</span> - <span class="badgecount">{{user.bronze}}</span> +{%- endif -%} +{% if badges_mode == 'public' %} + {%- if user.gold or user.silver or user.bronze %} + <a class="user-micro-info" + href="{{user.get_absolute_url()}}#badges" + ><span title="{{user.get_badge_summary}}">{% trans %}badges:{% endtrans %} + {% if user.gold %} + <span class='badge1'>●</span> + <span class="badgecount">{{user.gold}}</span> + {% endif %} + {% if user.silver %} + <span class='badge2'>●</span> + <span class="badgecount">{{user.silver}}</span> + {% endif %} + {% if user.bronze %} + <span class='badge3'>●</span> + <span class="badgecount">{{user.bronze}}</span> + {%- endif -%} + </span></a> {%- endif -%} -</span></a> {%- endif -%} diff --git a/askbot/skins/default/templates/widgets/user_navigation.html b/askbot/skins/default/templates/widgets/user_navigation.html index e79a482e..eec7e628 100644 --- a/askbot/skins/default/templates/widgets/user_navigation.html +++ b/askbot/skins/default/templates/widgets/user_navigation.html @@ -1,9 +1,17 @@ -{% if request.user.is_authenticated() %} +{%- if request.user.is_authenticated() -%} <a href="{{ request.user.get_absolute_url() }}">{{ request.user.username }}</a> <span class="user-info"> {{ macros.inbox_link(request.user) }} {{ macros.moderation_items_link(request.user, moderation_items) }} - ({{ macros.user_long_score_and_badge_summary(user) }}) + {%- + if settings.KARMA_MODE != 'hidden' and settings.BADGES_MODE != 'hidden' + -%} + ({{ macros.user_long_score_and_badge_summary( + user, + badges_mode = settings.BADGES_MODE + ) + }}) + {%- endif -%} </span> {% if settings.USE_ASKBOT_LOGIN_SYSTEM %} <a href="{{ settings.LOGOUT_URL }}?next={{ settings.LOGOUT_REDIRECT_URL }}">{% trans %}sign out{% endtrans %}</a> diff --git a/askbot/skins/default/templates/widgets/user_score_and_badge_summary.html b/askbot/skins/default/templates/widgets/user_score_and_badge_summary.html index 2f55b202..80d140db 100644 --- a/askbot/skins/default/templates/widgets/user_score_and_badge_summary.html +++ b/askbot/skins/default/templates/widgets/user_score_and_badge_summary.html @@ -1,19 +1,23 @@ +{% if karma_mode == 'public' %} <span class="reputation-score" title="{{user.get_karma_summary}}" >{{user.reputation}}</span> -{% if user.gold or user.silver or user.bronze %} -<span title="{{user.get_badge_summary}}"> - {% if user.gold %} - <span class='badge1'>●</span> - <span class="badgecount">{{user.gold}}</span> - {% endif %} - {% if user.silver %} - <span class='badge2'>●</span> - <span class="badgecount">{{user.silver}}</span> - {% endif %} - {% if user.bronze %} - <span class='badge3'>●</span> - <span class="badgecount">{{user.bronze}}</span> +{% endif %} +{% if badges_mode == 'public' %} + {% if user.gold or user.silver or user.bronze %} + <span title="{{user.get_badge_summary}}"> + {% if user.gold %} + <span class='badge1'>●</span> + <span class="badgecount">{{user.gold}}</span> + {% endif %} + {% if user.silver %} + <span class='badge2'>●</span> + <span class="badgecount">{{user.silver}}</span> + {% endif %} + {% if user.bronze %} + <span class='badge3'>●</span> + <span class="badgecount">{{user.bronze}}</span> + {% endif %} + </span> {% endif %} -</span> {% endif %} diff --git a/askbot/skins/utils.py b/askbot/skins/utils.py index 4f8e1992..0c0dba9c 100644 --- a/askbot/skins/utils.py +++ b/askbot/skins/utils.py @@ -73,7 +73,9 @@ def get_path_to_skin(skin): def get_skin_choices(): """returns a tuple for use as a set of choices in the form""" - skin_names = list(reversed(get_available_skins().keys())) + available_skins = get_available_skins().keys() + available_skins.remove('common') + skin_names = list(reversed(available_skins)) return zip(skin_names, skin_names) def resolve_skin_for_media(media=None, preferred_skin = None): @@ -192,25 +194,6 @@ def update_media_revision(skin = None): current_hash = hasher.get_hash_of_dirs(media_dirs) if current_hash != askbot_settings.MEDIA_RESOURCE_REVISION_HASH: - try: - askbot_settings.update('MEDIA_RESOURCE_REVISION', resource_revision + 1) - logging.debug('media revision worked for MEDIA_RESOURCE_REVISION') - except Exception, e: - logging.critical(e.message) - safe_settings_update('MEDIA_RESOURCE_REVISION', resource_revision + 1) - - try: - askbot_settings.update('MEDIA_RESOURCE_REVISION_HASH', current_hash) - logging.debug('media revision worked for MEDIA_RESOURCE_REVISION_HASH') - except Exception, e: - logging.critical(e.message) - safe_settings_update('MEDIA_RESOURCE_REVISION_HASH', current_hash) + askbot_settings.update('MEDIA_RESOURCE_REVISION', resource_revision + 1) + askbot_settings.update('MEDIA_RESOURCE_REVISION_HASH', current_hash) logging.debug('MEDIA_RESOURCE_REVISION changed') - - -def safe_settings_update(key, value): - '''Fallback when IntegrityError bug raises''' - from askbot.deps.livesettings.models import Setting - setting = Setting.objects.get(key=key) - setting.value = value - setting.save() diff --git a/askbot/templatetags/extra_filters_jinja.py b/askbot/templatetags/extra_filters_jinja.py index 8083657d..b03e4a89 100644 --- a/askbot/templatetags/extra_filters_jinja.py +++ b/askbot/templatetags/extra_filters_jinja.py @@ -1,4 +1,5 @@ import datetime +import pytz import re import time import urllib @@ -35,6 +36,16 @@ def absolutize_urls_func(text): return url_re4.sub(replacement, text) absolutize_urls = register.filter(absolutize_urls_func) +TIMEZONE_STR = pytz.timezone( + django_settings.TIME_ZONE + ).localize( + datetime.datetime.now() + ).strftime('%z') + +@register.filter +def add_tz_offset(datetime_object): + return str(datetime_object) + ' ' + TIMEZONE_STR + @register.filter def strip_path(url): """removes path part of the url""" diff --git a/askbot/tests/form_tests.py b/askbot/tests/form_tests.py index 4c67c1ff..654272b3 100644 --- a/askbot/tests/form_tests.py +++ b/askbot/tests/form_tests.py @@ -47,6 +47,7 @@ class AskByEmailFormTests(AskbotTestCase): 'subject': '[tag-one] where is titanic?', 'body_text': 'where is titanic?' } + def test_subject_line(self): """loops through various forms of the subject line and makes sure that tags and title are parsed out""" diff --git a/askbot/tests/page_load_tests.py b/askbot/tests/page_load_tests.py index 558ee617..ebfba0c3 100644 --- a/askbot/tests/page_load_tests.py +++ b/askbot/tests/page_load_tests.py @@ -553,19 +553,17 @@ class QuestionPageRedirectTests(AskbotTestCase): url = reverse('question', kwargs={'id': self.q.id}) resp = self.client.get(url) - url = url + self.q.slug - self.assertRedirects(resp, expected_url=url) - - resp = self.client.get(url) - self.assertEqual(200, resp.status_code) - self.assertEqual(self.q, resp.context['question']) + self.assertRedirects( + resp, + expected_url=self.q.get_absolute_url() + ) url = reverse('question', kwargs={'id': 101}) resp = self.client.get(url) - url = reverse('question', kwargs={'id': self.q.id}) + self.q.slug # redirect uses the new question.id ! + url = reverse('question', kwargs={'id': self.q.id}) + self.q.slug + '/'# redirect uses the new question.id ! self.assertRedirects(resp, expected_url=url) - url = reverse('question', kwargs={'id': 101}) + self.q.slug + url = reverse('question', kwargs={'id': 101}) + self.q.slug + '/' resp = self.client.get(url) self.assertEqual(200, resp.status_code) self.assertEqual(self.q, resp.context['question']) @@ -578,7 +576,7 @@ class QuestionPageRedirectTests(AskbotTestCase): url = reverse('question', kwargs={'id': self.q.id}) resp = self.client.get(url, data={'answer': self.a.id}) - url = url + self.q.slug + url = self.q.get_absolute_url() self.assertRedirects(resp, expected_url=url + '?answer=%d' % self.a.id) resp = self.client.get(url, data={'answer': self.a.id}) @@ -586,7 +584,8 @@ class QuestionPageRedirectTests(AskbotTestCase): self.assertEqual(self.q, resp.context['question']) self.assertEqual(self.a, resp.context['show_post']) - url = reverse('question', kwargs={'id': 101}) + self.q.slug + #test redirect from old question + url = reverse('question', kwargs={'id': 101}) + self.q.slug + '/' resp = self.client.get(url, data={'answer': 201}) self.assertRedirects(resp, expected_url=self.a.get_absolute_url()) @@ -597,10 +596,9 @@ class QuestionPageRedirectTests(AskbotTestCase): self.assertEqual(self.a, resp.context['show_post']) self.assertEqual(self.c, resp.context['show_comment']) - url = reverse('question', kwargs={'id': self.q.id}) + url = self.q.get_absolute_url() resp = self.client.get(url, data={'comment': self.c.id}) - url = url + self.q.slug - self.assertRedirects(resp, expected_url=url + '?comment=%d' % self.c.id) + self.assertEqual(200, resp.status_code) resp = self.client.get(url, data={'comment': self.c.id}) self.assertEqual(200, resp.status_code) @@ -608,6 +606,7 @@ class QuestionPageRedirectTests(AskbotTestCase): self.assertEqual(self.a, resp.context['show_post']) self.assertEqual(self.c, resp.context['show_comment']) - url = reverse('question', kwargs={'id': 101}) + self.q.slug - resp = self.client.get(url, data={'comment': 301}) - self.assertRedirects(resp, expected_url=self.c.get_absolute_url()) + url = self.q.get_absolute_url() + #point to a non-existing comment + resp = self.client.get(url, data={'comment': 100301}) + self.assertRedirects(resp, expected_url = self.q.get_absolute_url()) diff --git a/askbot/tests/post_model_tests.py b/askbot/tests/post_model_tests.py index 06bceca1..dd1399c1 100644 --- a/askbot/tests/post_model_tests.py +++ b/askbot/tests/post_model_tests.py @@ -167,17 +167,17 @@ class PostModelTests(AskbotTestCase): th.title = 'lala-x-lala' p = Post(id=3, post_type='question') p._thread_cache = th # cannot assign non-Thread instance directly - self.assertEqual('/question/3/lala-x-lala', p.get_absolute_url(thread=th)) + self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th)) self.assertTrue(p._thread_cache is th) - self.assertEqual('/question/3/lala-x-lala', p.get_absolute_url(thread=th)) + self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th)) def test_cached_get_absolute_url_2(self): p = Post(id=3, post_type='question') th = lambda:1 th.title = 'lala-x-lala' - self.assertEqual('/question/3/lala-x-lala', p.get_absolute_url(thread=th)) + self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th)) self.assertTrue(p._thread_cache is th) - self.assertEqual('/question/3/lala-x-lala', p.get_absolute_url(thread=th)) + self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th)) class ThreadTagModelsTests(AskbotTestCase): @@ -673,4 +673,4 @@ class ThreadRenderCacheUpdateTests(AskbotTestCase): # TODO: (in spare time - those cases should pass without changing anything in code but we should have them eventually for completness) # - Publishing anonymous questions / answers # - Re-posting question as answer and vice versa -# - Management commands (like post_emailed_questions)
\ No newline at end of file +# - Management commands (like post_emailed_questions) diff --git a/askbot/tests/reply_by_email_tests.py b/askbot/tests/reply_by_email_tests.py index 5128c9e7..a1272b0d 100644 --- a/askbot/tests/reply_by_email_tests.py +++ b/askbot/tests/reply_by_email_tests.py @@ -1,15 +1,31 @@ from django.utils.translation import ugettext as _ from askbot.models import ReplyAddress from askbot.lamson_handlers import PROCESS +from askbot import const from askbot.tests.utils import AskbotTestCase from askbot.models import Post, PostRevision +TEST_CONTENT = 'Test content' +TEST_EMAIL_PARTS = ( + ('body', TEST_CONTENT), +) +TEST_LONG_CONTENT = 'Test content' * 10 +TEST_LONG_EMAIL_PARTS = ( + ('body', TEST_LONG_CONTENT), +) + +class MockPart(object): + def __init__(self, body): + self.body = body + self.content_encoding = {'Content-Type':('text/plain',)} + class MockMessage(object): def __init__(self, body, from_email): self._body = body + self._part = MockPart(body) self.From= from_email def body(self): @@ -17,7 +33,7 @@ class MockMessage(object): def walk(self): """todo: add real file attachment""" - return list() + return [self._part] class EmailProcessingTests(AskbotTestCase): @@ -46,9 +62,15 @@ class EmailProcessingTests(AskbotTestCase): def test_process_correct_answer_comment(self): addr = ReplyAddress.objects.create_new( self.answer, self.u1).address - separator = _("======= Reply above this line. ====-=-=") - msg = MockMessage("This is a test reply \n\nOn such and such someone\ - wrote something \n\n%s\nlorem ipsum "%(separator), "user1@domain.com") + reply_separator = const.REPLY_SEPARATOR_TEMPLATE % { + 'user_action': 'john did something', + 'instruction': 'reply above this line' + } + msg = MockMessage( + "This is a test reply \n\nOn such and such someone" + "wrote something \n\n%s\nlorem ipsum " % (reply_separator), + "user1@domain.com" + ) PROCESS(msg, addr, '') self.assertEquals(self.answer.comments.count(), 2) self.assertEquals(self.answer.comments.all().order_by('-pk')[0].text.strip(), "This is a test reply") @@ -86,31 +108,27 @@ class ReplyAddressModelTests(AskbotTestCase): def test_create_answer_reply(self): result = ReplyAddress.objects.create_new( self.answer, self.u1) - post = result.create_reply("A test post") + post = result.create_reply(TEST_EMAIL_PARTS) self.assertEquals(post.post_type, "comment") - self.assertEquals(post.text, "A test post") + self.assertEquals(post.text, TEST_CONTENT) self.assertEquals(self.answer.comments.count(), 2) def test_create_comment_reply(self): result = ReplyAddress.objects.create_new( self.comment, self.u1) - post = result.create_reply("A test reply") + post = result.create_reply(TEST_EMAIL_PARTS) self.assertEquals(post.post_type, "comment") - self.assertEquals(post.text, "A test reply") + self.assertEquals(post.text, TEST_CONTENT) self.assertEquals(self.answer.comments.count(), 2) def test_create_question_comment_reply(self): result = ReplyAddress.objects.create_new( self.question, self.u3) - post = result.create_reply("A test post") + post = result.create_reply(TEST_EMAIL_PARTS) self.assertEquals(post.post_type, "comment") - self.assertEquals(post.text, "A test post") + self.assertEquals(post.text, TEST_CONTENT) def test_create_question_answer_reply(self): result = ReplyAddress.objects.create_new( self.question, self.u3) - post = result.create_reply("A test post "* 10) + post = result.create_reply(TEST_LONG_EMAIL_PARTS) self.assertEquals(post.post_type, "answer") - self.assertEquals(post.text, "A test post "* 10) - - - - + self.assertEquals(post.text, TEST_LONG_CONTENT) diff --git a/askbot/urls.py b/askbot/urls.py index 1ab3ea5d..799cc346 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -50,7 +50,7 @@ urlpatterns = patterns('', url( r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('revisions/')), views.readers.revisions, - kwargs = {'object_name': 'Answer'}, + kwargs = {'post_type': 'answer'}, name='answer_revisions' ), @@ -116,7 +116,7 @@ urlpatterns = patterns('', url( r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), views.readers.revisions, - kwargs = {'object_name': 'Question'}, + kwargs = {'post_type': 'question'}, name='question_revisions' ), url( @@ -159,6 +159,7 @@ urlpatterns = patterns('', views.readers.tags, name='tags' ), + #todo: collapse these three urls and use an extra json data var url(#ajax only r'^%s%s$' % ('mark-tag/', 'interesting/'), views.commands.mark_tag, @@ -172,6 +173,12 @@ urlpatterns = patterns('', name='mark_ignored_tag' ), url(#ajax only + r'^%s%s$' % ('mark-tag/', 'subscribed/'), + views.commands.mark_tag, + kwargs={'reason':'subscribed','action':'add'}, + name='mark_subscribed_tag' + ), + url(#ajax only r'^unmark-tag/', views.commands.mark_tag, kwargs={'action':'remove'}, @@ -193,6 +200,41 @@ urlpatterns = patterns('', name = 'get_tag_list' ), url( + r'^load-tag-wiki-text/', + views.commands.load_tag_wiki_text, + name = 'load_tag_wiki_text' + ), + url(#ajax only + r'^save-tag-wiki-text/', + views.commands.save_tag_wiki_text, + name = 'save_tag_wiki_text' + ), + url(#ajax only + r'^save-group-logo-url/', + views.commands.save_group_logo_url, + name = 'save_group_logo_url' + ), + url(#ajax only + r'^delete-group-logo/', + views.commands.delete_group_logo, + name = 'delete_group_logo' + ), + url(#ajax only + r'^toggle-group-profile-property/', + views.commands.toggle_group_profile_property, + name = 'toggle_group_profile_property' + ), + url(#ajax only + r'^edit-object-property-text/', + views.commands.edit_object_property_text, + name = 'edit_object_property_text' + ), + url( + r'^get-groups-list/', + views.commands.get_groups_list, + name = 'get_groups_list' + ), + url( r'^swap-question-with-answer/', views.commands.swap_question_with_answer, name = 'swap_question_with_answer' @@ -207,11 +249,17 @@ urlpatterns = patterns('', views.users.users, name='users' ), + url( + r'^%s%s(?P<group_id>\d+)/(?P<group_slug>.*)/$' % (_('users/'), _('by-group/')), + views.users.users, + kwargs = {'by_group': True}, + name = 'users_by_group' + ), #todo: rename as user_edit, b/c that's how template is named url( r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')), views.users.edit_user, - name='edit_user' + name ='edit_user' ), url( r'^%s(?P<id>\d+)/(?P<slug>.+)/%s$' % ( @@ -228,6 +276,11 @@ urlpatterns = patterns('', name='user_profile' ), url( + r'^%s$' % _('groups/'), + views.users.groups, + name='groups' + ), + url( r'^%s$' % _('users/update_has_custom_avatar/'), views.users.update_has_custom_avatar, name='user_update_has_custom_avatar' @@ -248,10 +301,30 @@ urlpatterns = patterns('', name='read_message' ), url(#ajax only - r'^manage_inbox/$', + r'^manage-inbox/$', views.commands.manage_inbox, name='manage_inbox' ), + url(#ajax only + r'^save-post-reject-reason/$', + views.commands.save_post_reject_reason, + name='save_post_reject_reason' + ), + url(#ajax only + r'^delete-post-reject-reason/$', + views.commands.delete_post_reject_reason, + name='delete_post_reject_reason' + ), + url(#ajax only + r'^edit-group-membership/$', + views.commands.edit_group_membership, + name='edit_group_membership' + ), + url(#ajax only + r'^join-or-leave-group/$', + views.commands.join_or_leave_group, + name = 'join_or_leave_group' + ), url( r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', @@ -294,6 +367,8 @@ urlpatterns = patterns('', ), ) +#todo - this url below won't work, because it is defined above +#therefore the stackexchange urls feature won't work if getattr(settings, 'ASKBOT_USE_STACKEXCHANGE_URLS', False): urlpatterns += (url( r'^%s(?P<id>\d+)/' % _('questions/'), diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py index 29e92645..940c3654 100644 --- a/askbot/utils/decorators.py +++ b/askbot/utils/decorators.py @@ -84,8 +84,20 @@ def ajax_only(view_func): raise Http404 try: data = view_func(request, *args, **kwargs) + if data is None: + data = {} except Exception, e: - message = unicode(e) + if isinstance(e, Exception): + if len(e.messages) > 1: + message = u'<ul>' + \ + u''.join( + map(lambda v: u'<li>%s</li>' % v, e.messages) + ) + \ + u'</ul>' + else: + message = e.messages[0] + else: + message = unicode(e) if message == '': message = _('Oops, apologies - there was some error') logging.debug(message) @@ -218,3 +230,16 @@ def check_spam(field): return wrapper return decorator + +def admins_only(view_func): + @functools.wraps(view_func) + def decorator(request, *args, **kwargs): + if request.user.is_anonymous(): + raise django_exceptions.PermissionDenied() + if not request.user.is_administrator_or_moderator(): + raise django_exceptions.PermissionDenied( + _('This function is limited to moderators and administrators') + ) + return view_func(request, *args, **kwargs) + return decorator + diff --git a/askbot/utils/file_utils.py b/askbot/utils/file_utils.py index daca1522..3793b5ce 100644 --- a/askbot/utils/file_utils.py +++ b/askbot/utils/file_utils.py @@ -5,7 +5,7 @@ import time import urlparse from django.core.files.storage import get_storage_class -def store_file(file_object): +def store_file(file_object, file_name_prefix = ''): """Creates an instance of django's file storage object based on the file-like object, returns the storage object, file name, file url @@ -16,6 +16,7 @@ def store_file(file_object): '.', str(random.randint(0,100000)) ) + os.path.splitext(file_object.name)[1].lower() + file_name = file_name_prefix + file_name file_storage = get_storage_class()() # use default storage to store file diff --git a/askbot/utils/functions.py b/askbot/utils/functions.py index 6042414c..f9d36534 100644 --- a/askbot/utils/functions.py +++ b/askbot/utils/functions.py @@ -18,6 +18,25 @@ def enumerate_string_list(strings): numbered_strings = enumerate(strings, start = 1) return [ '%d) %s' % item for item in numbered_strings ] +def pad_string(text): + """Inserts one space between words, + including one space before the first word + and after the last word. + String without words is collapsed to '' + """ + words = text.strip().split() + if len(words) > 0: + return ' ' + ' '.join(words) + ' ' + else: + return '' + +def split_list(text): + """Takes text, representing a loosely formatted + list (comma, semicolon, empty space separated + words) and returns a list() of words. + """ + text = text.replace(',', ' ').replace(';', ' ') + return text.strip().split() def is_iterable(thing): if hasattr(thing, '__iter__'): diff --git a/askbot/utils/mail.py b/askbot/utils/mail.py index e4fb7854..37245b5a 100644 --- a/askbot/utils/mail.py +++ b/askbot/utils/mail.py @@ -7,6 +7,7 @@ import logging from django.core import mail from django.conf import settings as django_settings from django.core.exceptions import PermissionDenied +from django.forms import ValidationError from django.utils.translation import ugettext_lazy as _ from django.utils.translation import string_concat from askbot import exceptions @@ -197,78 +198,112 @@ def bounce_email(email, subject, reason = None, body_text = None): body_text = error_message ) -def process_attachments(attachments): - """saves file attachments and adds - - cheap way of dealing with the attachments - just insert them inline, however it might - be useful to keep track of the uploaded files separately - and deal with them as with resources of their own value""" - if attachments: - content = '' - for att in attachments: - file_storage, file_name, file_url = store_file(att) - chunk = '[%s](%s) ' % (att.name, file_url) - file_extension = os.path.splitext(att.name)[1] - #todo: this is a hack - use content type - if file_extension.lower() in ('.png', '.jpg', '.gif'): - chunk = '\n\n!' + chunk - content += '\n\n' + chunk - return content +def extract_reply(text): + """take the part above the separator + and discard the last line above the separator""" + if const.REPLY_SEPARATOR_REGEX.search(text): + text = const.REPLY_SEPARATOR_REGEX.split(text)[0] + return '\n'.join(text.splitlines(True)[:-3]) else: - return '' + return text +def process_attachment(attachment): + """will save a single + attachment and return + link to file in the markdown format and the + file storage object + """ + file_storage, file_name, file_url = store_file(attachment) + markdown_link = '[%s](%s) ' % (attachment.name, file_url) + file_extension = os.path.splitext(attachment.name)[1] + #todo: this is a hack - use content type + if file_extension.lower() in ('.png', '.jpg', '.jpeg', '.gif'): + markdown_link = '!' + markdown_link + return markdown_link, file_storage + +def process_parts(parts): + """Process parts will upload the attachments and parse out the + body, if body is multipart. Secondly - links to attachments + will be added to the body of the question. + Returns ready to post body of the message and the list + of uploaded files. + """ + body_markdown = '' + stored_files = list() + attachments_markdown = '' + for (part_type, content) in parts: + if part_type == 'attachment': + markdown, stored_file = process_attachment(content) + stored_files.append(stored_file) + attachments_markdown += '\n\n' + markdown + elif part_type == 'body': + body_markdown += '\n\n' + content + elif part_type == 'inline': + markdown, stored_file = process_attachment(content) + stored_files.append(stored_file) + body_markdown += markdown + + #if the response separator is present - + #split the body with it, and discard the "so and so wrote:" part + body_markdown = extract_reply(body_markdown) + body_markdown += attachments_markdown + return body_markdown.strip(), stored_files -def process_emailed_question(from_address, subject, body, attachments = None): + +def process_emailed_question(from_address, subject, parts, tags = None): """posts question received by email or bounces the message""" #a bunch of imports here, to avoid potential circular import issues from askbot.forms import AskByEmailForm from askbot.models import User - data = { - 'sender': from_address, - 'subject': subject, - 'body_text': body - } - form = AskByEmailForm(data) - if form.is_valid(): - email_address = form.cleaned_data['email'] - try: + + try: + #todo: delete uploaded files when posting by email fails!!! + body, stored_files = process_parts(parts) + data = { + 'sender': from_address, + 'subject': subject, + 'body_text': body + } + form = AskByEmailForm(data) + if form.is_valid(): + email_address = form.cleaned_data['email'] user = User.objects.get( email__iexact = email_address ) - except User.DoesNotExist: - bounce_email(email_address, subject, reason = 'unknown_user') - except User.MultipleObjectsReturned: - bounce_email(email_address, subject, reason = 'problem_posting') + tagnames = form.cleaned_data['tagnames'] + title = form.cleaned_data['title'] + body_text = form.cleaned_data['body_text'] - tagnames = form.cleaned_data['tagnames'] - title = form.cleaned_data['title'] - body_text = form.cleaned_data['body_text'] + #defect - here we might get "too many tags" issue + if tags: + tagnames += ' ' + ' '.join(tags) - try: - body_text += process_attachments(attachments) user.post_question( title = title, tags = tagnames, - body_text = body_text - ) - except PermissionDenied, error: - bounce_email( - email_address, - subject, - reason = 'permission_denied', - body_text = unicode(error) + body_text = body_text, + by_email = True, + email_address = from_address ) - else: - #error_list = list() - #for field_errors in form.errors.values(): - # error_list.extend(field_errors) + else: + raise ValidationError() + except User.DoesNotExist: + bounce_email(email_address, subject, reason = 'unknown_user') + except User.MultipleObjectsReturned: + bounce_email(email_address, subject, reason = 'problem_posting') + except PermissionDenied, error: + bounce_email( + email_address, + subject, + reason = 'permission_denied', + body_text = unicode(error) + ) + except ValidationError: if from_address: bounce_email( from_address, subject, reason = 'problem_posting', - #body_text = '\n*'.join(error_list) ) diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 9ed88225..0bb64475 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -10,18 +10,20 @@ from django.core import exceptions from django.core.urlresolvers import reverse from django.contrib.auth.decorators import login_required from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponseBadRequest -from django.forms import ValidationError +from django.forms import ValidationError, IntegerField, CharField from django.shortcuts import get_object_or_404 from django.views.decorators import csrf from django.utils import simplejson from django.utils.translation import ugettext as _ +from django.utils.translation import string_concat from askbot import models from askbot import forms from askbot.conf import should_show_sort_by_relevance from askbot.conf import settings as askbot_settings from askbot.utils import decorators from askbot.utils import url_utils -from askbot.skins.loaders import render_into_skin +from askbot.utils import mail +from askbot.skins.loaders import render_into_skin, get_template from askbot import const import logging @@ -41,7 +43,12 @@ def manage_inbox(request): post_data = simplejson.loads(request.raw_post_data) if request.user.is_authenticated(): activity_types = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY - activity_types += (const.TYPE_ACTIVITY_MENTION, const.TYPE_ACTIVITY_MARK_OFFENSIVE,) + activity_types += ( + const.TYPE_ACTIVITY_MENTION, + const.TYPE_ACTIVITY_MARK_OFFENSIVE, + const.TYPE_ACTIVITY_MODERATED_NEW_POST, + const.TYPE_ACTIVITY_MODERATED_POST_EDIT + ) user = request.user memo_set = models.ActivityAuditStatus.objects.filter( id__in = post_data['memo_list'], @@ -58,20 +65,54 @@ def manage_inbox(request): memo_set.update(status = models.ActivityAuditStatus.STATUS_SEEN) elif action_type == 'remove_flag': for memo in memo_set: - request.user.flag_post(post = memo.activity.content_object, cancel_all = True) - elif action_type == 'close': - for memo in memo_set: - if memo.activity.content_object.post_type == "question": - request.user.close_question(question = memo.activity.content_object, reason = 7) + activity_type = memo.activity.activity_type + if activity_type == const.TYPE_ACTIVITY_MARK_OFFENSIVE: + request.user.flag_post( + post = memo.activity.content_object, + cancel_all = True + ) + elif activity_type in \ + ( + const.TYPE_ACTIVITY_MODERATED_NEW_POST, + const.TYPE_ACTIVITY_MODERATED_POST_EDIT + ): + post_revision = memo.activity.content_object + request.user.approve_post_revision(post_revision) memo.delete() + + #elif action_type == 'close': + # for memo in memo_set: + # if memo.activity.content_object.post_type == "question": + # request.user.close_question(question = memo.activity.content_object, reason = 7) + # memo.delete() elif action_type == 'delete_post': for memo in memo_set: - request.user.delete_post(post = memo.activity.content_object) + content_object = memo.activity.content_object + if isinstance(content_object, models.PostRevision): + post = content_object.post + else: + post = content_object + request.user.delete_post(post) + reject_reason = models.PostFlagReason.objects.get( + id = post_data['reject_reason_id'] + ) + body_text = string_concat( + _('Your post (copied in the end),'), + '<br/>', + _('was rejected for the following reason:'), + '<br/><br/>', + reject_reason.details.html, + '<br/><br/>', + _('Here is your original post'), + '<br/><br/>', + post.text + ) + mail.send_mail( + subject_line = _('your post was not accepted'), + body_text = unicode(body_text), + recipient_list = [post.author,] + ) memo.delete() - else: - raise exceptions.PermissionDenied( - _('Oops, apologies - there was some error') - ) user.update_response_counts() @@ -439,7 +480,49 @@ def get_tag_list(request): 'name', flat = True ) output = '\n'.join(tag_names) - return HttpResponse(output, mimetype = "text/plain") + return HttpResponse(output, mimetype = 'text/plain') + +@decorators.get_only +def load_tag_wiki_text(request): + """returns text of the tag wiki in markdown format""" + tag = get_object_or_404(models.Tag, id = request.GET['tag_id']) + tag_wiki_text = getattr(tag.tag_wiki, 'text', '').strip() + return HttpResponse(tag_wiki_text, mimetype = 'text/plain') + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +def save_tag_wiki_text(request): + """if tag wiki text does not exist, + creates a new record, otherwise edits an existing + tag wiki record""" + form = forms.EditTagWikiForm(request.POST) + if form.is_valid(): + tag_id = form.cleaned_data['tag_id'] + text = form.cleaned_data['text'] or ' '#a hack to save blank data + tag = models.Tag.objects.get(id = tag_id) + if tag.tag_wiki: + request.user.edit_post(tag.tag_wiki, body_text = text) + tag_wiki = tag.tag_wiki + else: + tag_wiki = request.user.post_tag_wiki(tag, body_text = text) + return {'html': tag_wiki.html} + else: + raise ValueError('invalid post data') + + +@decorators.get_only +def get_groups_list(request): + """returns names of group tags + for the autocomplete function""" + group_names = models.Tag.group_tags.get_all().filter( + deleted = False + ).values_list( + 'name', flat = True + ) + group_names = map(lambda v: v.replace('-', ' '), group_names) + output = '\n'.join(group_names) + return HttpResponse(output, mimetype = 'text/plain') @csrf.csrf_protect def subscribe_for_tags(request): @@ -501,14 +584,18 @@ def api_get_questions(request): @decorators.post_only @decorators.ajax_login_required def set_tag_filter_strategy(request): - """saves data in the ``User.display_tag_filter_strategy`` + """saves data in the ``User.[email/display]_tag_filter_strategy`` for the current user """ filter_type = request.POST['filter_type'] filter_value = int(request.POST['filter_value']) - assert(filter_type == 'display') - assert(filter_value in dict(const.TAG_FILTER_STRATEGY_CHOICES)) - request.user.display_tag_filter_strategy = filter_value + assert(filter_type in ('display', 'email')) + if filter_type == 'display': + assert(filter_value in dict(const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES)) + request.user.display_tag_filter_strategy = filter_value + else: + assert(filter_value in dict(const.TAG_EMAIL_FILTER_STRATEGY_CHOICES)) + request.user.email_tag_filter_strategy = filter_value request.user.save() return HttpResponse('', mimetype = "application/json") @@ -643,3 +730,182 @@ def read_message(request):#marks message a read if request.user.is_authenticated(): request.user.delete_messages() return HttpResponse('') + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +@decorators.admins_only +def edit_group_membership(request): + form = forms.EditGroupMembershipForm(request.POST) + if form.is_valid(): + group_name = form.cleaned_data['group_name'] + user_id = form.cleaned_data['user_id'] + try: + user = models.User.objects.get(id = user_id) + except models.User.DoesNotExist: + raise exceptions.PermissionDenied( + 'user with id %d not found' % user_id + ) + + action = form.cleaned_data['action'] + #warning: possible race condition + if action == 'add': + group_params = {'group_name': group_name, 'user': user} + group = models.Tag.group_tags.get_or_create(**group_params) + request.user.edit_group_membership(user, group, 'add') + template = get_template('widgets/group_snippet.html') + return { + 'name': group.name, + 'description': getattr(group.tag_wiki, 'text', ''), + 'html': template.render({'group': group}) + } + elif action == 'remove': + try: + group = models.Tag.group_tags.get_by_name(group_name = group_name) + request.user.edit_group_membership(user, group, 'remove') + except models.Tag.DoesNotExist: + raise exceptions.PermissionDenied() + else: + raise exceptions.PermissionDenied() + else: + raise exceptions.PermissionDenied() + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +@decorators.admins_only +def save_group_logo_url(request): + """saves urls for the group logo""" + form = forms.GroupLogoURLForm(request.POST) + if form.is_valid(): + group_id = form.cleaned_data['group_id'] + image_url = form.cleaned_data['image_url'] + group = models.Tag.group_tags.get(id = group_id) + group.group_profile.logo_url = image_url + group.group_profile.save() + else: + raise ValueError('invalid data found when saving group logo') + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +@decorators.admins_only +def delete_group_logo(request): + group_id = IntegerField().clean(int(request.POST['group_id'])) + group = models.Tag.group_tags.get(id = group_id) + group.group_profile.logo_url = None + group.group_profile.save() + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +@decorators.admins_only +def delete_post_reject_reason(request): + reason_id = IntegerField().clean(int(request.POST['reason_id'])) + reason = models.PostFlagReason.objects.get(id = reason_id) + reason.delete() + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +@decorators.admins_only +def toggle_group_profile_property(request): + #todo: this might be changed to more general "toggle object property" + group_id = IntegerField().clean(int(request.POST['group_id'])) + property_name = CharField().clean(request.POST['property_name']) + assert property_name in ('is_open', 'moderate_email') + + group = models.Tag.objects.get(id = group_id) + new_value = not getattr(group.group_profile, property_name) + setattr(group.group_profile, property_name, new_value) + group.group_profile.save() + return {'is_enabled': new_value} + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.admins_only +def edit_object_property_text(request): + model_name = CharField().clean(request.REQUEST['model_name']) + object_id = IntegerField().clean(request.REQUEST['object_id']) + property_name = CharField().clean(request.REQUEST['property_name']) + + accessible_fields = ( + ('GroupProfile', 'preapproved_emails'), + ('GroupProfile', 'preapproved_email_domains') + ) + + if (model_name, property_name) not in accessible_fields: + raise exceptions.PermissionDenied() + + obj = models.get_model(model_name).objects.get(id=object_id) + if request.method == 'POST': + text = CharField().clean(request.POST['text']) + setattr(obj, property_name, text) + obj.save() + elif request.method == 'GET': + return {'text': getattr(obj, property_name)} + else: + raise exceptions.PermissionDenied() + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +def join_or_leave_group(request): + """only current user can join/leave group""" + if request.user.is_anonymous(): + raise exceptions.PermissionDenied() + + group_id = IntegerField().clean(request.POST['group_id']) + group = models.Tag.objects.get(id = group_id) + + if request.user.is_group_member(group): + action = 'remove' + is_member = False + else: + action = 'add' + is_member = True + request.user.edit_group_membership( + user = request.user, + group = group, + action = action + ) + return {'is_member': is_member} + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only +@decorators.admins_only +def save_post_reject_reason(request): + """saves post reject reason and returns the reason id + if reason_id is not given in the input - a new reason is created, + otherwise a reason with the given id is edited and saved + """ + form = forms.EditRejectReasonForm(request.POST) + if form.is_valid(): + title = form.cleaned_data['title'] + details = form.cleaned_data['details'] + if form.cleaned_data['reason_id'] is None: + reason = request.user.create_post_reject_reason( + title = title, details = details + ) + else: + reason_id = form.cleaned_data['reason_id'] + reason = models.PostFlagReason.objects.get(id = reason_id) + request.user.edit_post_reject_reason( + reason, title = title, details = details + ) + return { + 'reason_id': reason.id, + 'title': title, + 'details': details + } + else: + raise Exception(forms.format_form_errors(form)) diff --git a/askbot/views/meta.py b/askbot/views/meta.py index 73cb494f..3e5a0b35 100644 --- a/askbot/views/meta.py +++ b/askbot/views/meta.py @@ -111,6 +111,8 @@ def privacy(request): def badges(request):#user status/reputation system #todo: supplement database data with the stuff from badges.py + if askbot_settings.BADGES_MODE != 'public': + raise Http404 known_badges = badge_data.BADGES.keys() badges = BadgeData.objects.filter(slug__in = known_badges).order_by('slug') my_badges = [] diff --git a/askbot/views/readers.py b/askbot/views/readers.py index 2a81f5c2..3259cddd 100644 --- a/askbot/views/readers.py +++ b/askbot/views/readers.py @@ -76,6 +76,9 @@ def questions(request, **kwargs): qs, meta_data = models.Thread.objects.run_advanced_search(request_user=request.user, search_state=search_state) + if meta_data['non_existing_tags']: + search_state = search_state.remove_tags(meta_data['non_existing_tags']) + paginator = Paginator(qs, page_size) if paginator.num_pages < search_state.page: search_state.page = 1 @@ -129,10 +132,7 @@ def questions(request, **kwargs): if request.is_ajax(): q_count = paginator.count - if search_state.tags: - question_counter = ungettext('%(q_num)s question, tagged', '%(q_num)s questions, tagged', q_count) - else: - question_counter = ungettext('%(q_num)s question', '%(q_num)s questions', q_count) + question_counter = ungettext('%(q_num)s question', '%(q_num)s questions', q_count) question_counter = question_counter % {'q_num': humanize.intcomma(q_count),} if q_count > page_size: @@ -166,6 +166,7 @@ def questions(request, **kwargs): 'query_string': search_state.query_string(), 'page_size' : page_size, 'questions': questions_html.replace('\n',''), + 'non_existing_tags': meta_data['non_existing_tags'] } ajax_data['related_tags'] = [{ 'name': tag.name, @@ -182,8 +183,9 @@ def questions(request, **kwargs): 'contributors' : contributors, 'context' : paginator_context, 'is_unanswered' : False,#remove this from template - 'interesting_tag_names': meta_data.get('interesting_tag_names',None), - 'ignored_tag_names': meta_data.get('ignored_tag_names',None), + 'interesting_tag_names': meta_data.get('interesting_tag_names', None), + 'ignored_tag_names': meta_data.get('ignored_tag_names', None), + 'subscribed_tag_names': meta_data.get('subscribed_tag_names', None), 'language_code': translation.get_language(), 'name_of_anonymous_user' : models.get_name_of_anonymous_user(), 'page_class': 'main-page', @@ -200,7 +202,8 @@ def questions(request, **kwargs): 'tags' : related_tags, 'tag_list_type' : tag_list_type, 'font_size' : extra_tags.get_tag_font_size(related_tags), - 'tag_filter_strategy_choices': const.TAG_FILTER_STRATEGY_CHOICES, + 'display_tag_filter_strategy_choices': const.TAG_DISPLAY_FILTER_STRATEGY_CHOICES, + 'email_tag_filter_strategy_choices': const.TAG_EMAIL_FILTER_STRATEGY_CHOICES, 'update_avatar_data': schedules.should_update_avatar_data(request), 'query_string': search_state.query_string(), 'search_state': search_state, @@ -356,7 +359,7 @@ def question(request, id):#refactor - long subroutine. display question body, an return HttpResponseRedirect(reverse('index')) #redirect if slug in the url is wrong - if request.path.split('/')[-1] != question_post.slug: + if request.path.split('/')[-2] != question_post.slug: logging.debug('no slug match!') question_url = '?'.join(( question_post.get_absolute_url(), @@ -389,7 +392,7 @@ def question(request, id):#refactor - long subroutine. display question body, an 'deleted and is no longer accessible' ) request.user.message_set.create(message = error_message) - return HttpResponseRedirect(reverse('question', kwargs = {'id': id})) + return HttpResponseRedirect(question_post.thread.get_absolute_url()) if str(show_comment.thread._question_post().id) != str(id): return HttpResponseRedirect(show_comment.get_absolute_url()) @@ -552,15 +555,12 @@ def question(request, id):#refactor - long subroutine. display question body, an #print datetime.datetime.now() - before return result -def revisions(request, id, object_name=None): - if object_name == 'Question': - post = get_object_or_404(models.Post, post_type='question', id=id) - else: - post = get_object_or_404(models.Post, post_type='answer', id=id) +def revisions(request, id, post_type = None): + assert post_type in ('question', 'answer') + post = get_object_or_404(models.Post, post_type=post_type, id=id) revisions = list(models.PostRevision.objects.filter(post=post)) revisions.reverse() for i, revision in enumerate(revisions): - revision.html = revision.as_html() if i == 0: revision.diff = revisions[i].html revision.summary = _('initial version') diff --git a/askbot/views/users.py b/askbot/views/users.py index 582bb2af..ef0aea57 100644 --- a/askbot/views/users.py +++ b/askbot/views/users.py @@ -55,9 +55,53 @@ def owner_or_moderator_required(f): return f(request, profile_owner, context) return wrapped_func -def users(request): +def users(request, by_group = False, group_id = None, group_slug = None): + """Users view, including listing of users by group""" + users = models.User.objects.all() + group = None + group_email_moderation_enabled = False + user_can_join_group = False + user_is_group_member = False + if by_group == True: + if askbot_settings.GROUPS_ENABLED == False: + raise Http404 + if group_id: + if all((group_id, group_slug)) == False: + return HttpResponseRedirect('groups') + else: + try: + group = models.Tag.group_tags.get(id = group_id) + group_email_moderation_enabled = \ + ( + askbot_settings.GROUP_EMAIL_ADDRESSES_ENABLED \ + and askbot_settings.ENABLE_CONTENT_MODERATION + ) + user_can_join_group = group.group_profile.can_accept_user(request.user) + except models.Tag.DoesNotExist: + raise Http404 + if group_slug == slugify(group.name): + users = models.User.objects.filter( + group_memberships__group__id = group_id + ) + if request.user.is_authenticated(): + user_is_group_member = bool(users.filter(id = request.user.id).count()) + else: + group_page_url = reverse( + 'users_by_group', + kwargs = { + 'group_id': group.id, + 'group_slug': slugify(group.name) + } + ) + return HttpResponseRedirect(group_page_url) + + is_paginated = True + sortby = request.GET.get('sort', 'reputation') + if askbot_settings.KARMA_MODE == 'private' and sortby == 'reputation': + sortby = 'newest' + suser = request.REQUEST.get('query', "") try: page = int(request.GET.get('page', '1')) @@ -76,23 +120,21 @@ def users(request): order_by_parameter = '-reputation' objects_list = Paginator( - models.User.objects.all().order_by( - order_by_parameter - ), + users.order_by(order_by_parameter), const.USERS_PAGE_SIZE ) - base_url = reverse('users') + '?sort=%s&' % sortby + base_url = request.path + '?sort=%s&' % sortby else: sortby = "reputation" objects_list = Paginator( - models.User.objects.filter( - username__icontains = suser - ).order_by( - '-reputation' - ), + users.filter( + username__icontains = suser + ).order_by( + '-reputation' + ), const.USERS_PAGE_SIZE ) - base_url = reverse('users') + '?name=%s&sort=%s&' % (suser, sortby) + base_url = request.path + '?name=%s&sort=%s&' % (suser, sortby) try: users_page = objects_list.page(page) @@ -114,10 +156,14 @@ def users(request): 'active_tab': 'users', 'page_class': 'users-page', 'users' : users_page, + 'group': group, 'suser' : suser, 'keywords' : suser, 'tab_id' : sortby, - 'paginator_context' : paginator_context + 'paginator_context' : paginator_context, + 'group_email_moderation_enabled': group_email_moderation_enabled, + 'user_can_join_group': user_can_join_group, + 'user_is_group_member': user_is_group_member } return render_into_skin('users.html', data, request) @@ -275,6 +321,9 @@ def user_stats(request, user, context): if request.user != user: question_filter['is_anonymous'] = False + if askbot_settings.ENABLE_CONTENT_MODERATION: + question_filter['approved'] = True + # # Questions # @@ -394,7 +443,7 @@ def user_stats(request, user, context): 'votes_total_per_day': votes_total, 'user_tags' : user_tags, - + 'user_groups': models.Tag.group_tags.get_for_user(user = user), 'badges': badges, 'total_badges' : len(badges), } @@ -563,20 +612,32 @@ def user_responses(request, user, context): as well as mentions of the user user - the profile owner + + the view has two sub-views - "forum" - i.e. responses + and "flags" - moderation items for mods only """ - section = 'forum' - if request.user.is_moderator() or request.user.is_administrator(): - if 'section' in request.GET and request.GET['section'] == 'flags': - section = 'flags' + #1) select activity types according to section + section = request.GET.get('section', 'forum') + if section == 'flags' and not\ + (request.user.is_moderator() or request.user.is_administrator()): + raise Http404 if section == 'forum': activity_types = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY activity_types += (const.TYPE_ACTIVITY_MENTION,) - else: - assert(section == 'flags') + elif section == 'flags': activity_types = (const.TYPE_ACTIVITY_MARK_OFFENSIVE,) + if askbot_settings.ENABLE_CONTENT_MODERATION: + activity_types += ( + const.TYPE_ACTIVITY_MODERATED_NEW_POST, + const.TYPE_ACTIVITY_MODERATED_POST_EDIT + ) + else: + raise Http404 + #2) load the activity notifications according to activity types + #todo: insert pagination code here memo_set = models.ActivityAuditStatus.objects.filter( user = request.user, activity__activity_type__in = activity_types @@ -590,17 +651,17 @@ def user_responses(request, user, context): '-activity__active_at' )[:const.USER_VIEW_DATA_SIZE] - #todo: insert pagination code here - + #3) "package" data for the output response_list = list() for memo in memo_set: + #a monster query chain below response = { 'id': memo.id, 'timestamp': memo.activity.active_at, 'user': memo.activity.user, 'is_new': memo.is_new(), 'response_url': memo.activity.get_absolute_url(), - 'response_snippet': memo.activity.get_preview(), + 'response_snippet': memo.activity.get_snippet(), 'response_title': memo.activity.question.thread.title, 'response_type': memo.activity.get_activity_type_display(), 'response_id': memo.activity.question.id, @@ -609,13 +670,14 @@ def user_responses(request, user, context): } response_list.append(response) + #4) sort by response id response_list.sort(lambda x,y: cmp(y['response_id'], x['response_id'])) + + #5) group responses by thread (response_id is really the question post id) last_response_id = None #flag to know if the response id is different - last_response_index = None #flag to know if the response index in the list is different filtered_response_list = list() - for i, response in enumerate(response_list): - #todo: agrupate users + #todo: group responses by the user as well if response['response_id'] == last_response_id: original_response = dict.copy(filtered_response_list[len(filtered_response_list)-1]) original_response['nested_responses'].append(response) @@ -623,13 +685,11 @@ def user_responses(request, user, context): else: filtered_response_list.append(response) last_response_id = response['response_id'] - last_response_index = i - response_list = filtered_response_list - - response_list.sort(lambda x,y: cmp(y['timestamp'], x['timestamp'])) - filtered_response_list = list() + #6) sort responses by time + filtered_response_list.sort(lambda x,y: cmp(y['timestamp'], x['timestamp'])) + reject_reasons = models.PostFlagReason.objects.all().order_by('title'); data = { 'active_tab':'users', 'page_class': 'user-profile-page', @@ -637,7 +697,8 @@ def user_responses(request, user, context): 'inbox_section':section, 'tab_description' : _('comments and answers to others questions'), 'page_title' : _('profile - responses'), - 'responses' : response_list, + 'post_reject_reasons': reject_reasons, + 'responses' : filtered_response_list, } context.update(data) return render_into_skin('user_profile/user_inbox.html', context, request) @@ -804,6 +865,20 @@ def user(request, id, slug=None, tab_name=None): if not tab_name: tab_name = request.GET.get('sort', 'stats') + if askbot_settings.KARMA_MODE == 'public': + can_show_karma = True + elif askbot_settings.KARMA_MODE == 'hidden': + can_show_karma = False + else: + if request.user.is_administrator_or_moderator() \ + or request.user == profile_owner: + can_show_karma = True + else: + can_show_karma = False + + if can_show_karma == False and tab_name == 'reputation': + raise Http404 + user_view_func = USER_VIEW_CALL_TABLE.get(tab_name, user_stats) search_state = SearchState( # Non-default SearchState with user data set @@ -818,6 +893,7 @@ def user(request, id, slug=None, tab_name=None): context = { 'view_user': profile_owner, + 'can_show_karma': can_show_karma, 'search_state': search_state, 'user_follow_feature_on': ('followit' in django_settings.INSTALLED_APPS), } @@ -833,3 +909,18 @@ def update_has_custom_avatar(request): 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 + """ + if askbot_settings.GROUPS_ENABLED == False: + raise Http404 + groups = models.Tag.group_tags.get_all() + can_edit = request.user.is_authenticated() and \ + request.user.is_administrator_or_moderator() + data = { + 'groups': groups, + 'can_edit': can_edit, + 'active_tab': 'users' + } + return render_into_skin('groups.html', data, request) diff --git a/askbot/views/writers.py b/askbot/views/writers.py index d9a6f855..7727eb04 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -62,9 +62,13 @@ def upload(request):#ajax upload file to a question or answer request.user.assert_can_upload_file() + #todo: build proper form validation + file_name_prefix = request.POST.get('file_name_prefix', '') + if file_name_prefix not in ('', 'group_logo_'): + raise exceptions.PermissionDenied('invalid upload file name prefix') + # check file type f = request.FILES['file-upload'] - #todo: extension checking should be replaced with mimetype checking #and this must be part of the form validation file_extension = os.path.splitext(f.name)[1].lower() @@ -75,7 +79,9 @@ def upload(request):#ajax upload file to a question or answer raise exceptions.PermissionDenied(msg) # generate new file name and storage object - file_storage, new_file_name, file_url = store_file(f) + file_storage, new_file_name, file_url = store_file( + f, file_name_prefix + ) # check file size # byte size = file_storage.size(new_file_name) @@ -451,6 +457,7 @@ def edit_answer(request, id): revision_form = forms.RevisionForm(answer, latest_revision) form = forms.EditAnswerForm(answer, latest_revision) data = { + 'page_class': 'edit-answer-page', 'active_tab': 'questions', 'answer': answer, 'revision_form': revision_form, @@ -533,9 +540,10 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po comment_owner = comment.author + tz = template_filters.TIMEZONE_STR comment_data = {'id' : comment.id, 'object_id': obj.id, - 'comment_age': diff_date(comment.added_at), + 'comment_added_at': str(comment.added_at.replace(microsecond = 0)) + tz, 'html': comment.html, 'user_display_name': comment_owner.username, 'user_url': comment_owner.get_profile_url(), @@ -599,7 +607,7 @@ def edit_comment(request): return { 'id' : comment_post.id, 'object_id': comment_post.parent.id, - 'comment_age': diff_date(comment_post.added_at), + 'comment_added_at': str(comment_post.added_at.replace(microsecond = 0)), 'html': comment_post.html, 'user_display_name': comment_post.author.username, 'user_url': comment_post.author.get_profile_url(), diff --git a/askbot_requirements.txt b/askbot_requirements.txt index 301fb93e..7b619c36 100644 --- a/askbot_requirements.txt +++ b/askbot_requirements.txt @@ -18,3 +18,4 @@ django-followit django-recaptcha-works python-openid pystache==0.3.1 +pytz diff --git a/askbot_requirements_dev.txt b/askbot_requirements_dev.txt index 199f0308..ada0d83e 100644 --- a/askbot_requirements_dev.txt +++ b/askbot_requirements_dev.txt @@ -19,3 +19,4 @@ django-recaptcha-works python-openid pystache==0.3.1 pylint +pytz |