summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdolfo Fitoria <adolfo.fitoria@gmail.com>2012-05-15 15:25:07 -0600
committerAdolfo Fitoria <adolfo.fitoria@gmail.com>2012-05-15 15:25:07 -0600
commitfe2bd64fcf4767c3801af4451e347365d288b15d (patch)
treeb61e513ee6bc0ff06a4a85e4d8f02f42941121b3
parent32a4a5ddf93f2bc605706ecc3eafbc63dc923acf (diff)
parent4cf3ec3998b1f067a7f0c76eae4a90c274024986 (diff)
downloadaskbot-fe2bd64fcf4767c3801af4451e347365d288b15d.tar.gz
askbot-fe2bd64fcf4767c3801af4451e347365d288b15d.tar.bz2
askbot-fe2bd64fcf4767c3801af4451e347365d288b15d.zip
Merge branch 'master' into livesettings_fix
-rw-r--r--askbot/__init__.py3
-rw-r--r--askbot/api.py8
-rw-r--r--askbot/askbot0
-rw-r--r--askbot/conf/__init__.py3
-rw-r--r--askbot/conf/forum_data_rules.py23
-rw-r--r--askbot/conf/group_settings.py32
-rw-r--r--askbot/conf/karma_and_badges_visibility.py50
-rw-r--r--askbot/conf/moderation.py22
-rw-r--r--askbot/conf/settings_wrapper.py10
-rw-r--r--askbot/conf/skin_general_settings.py4
-rw-r--r--askbot/const/__init__.py46
-rw-r--r--askbot/deps/livesettings/values.py6
-rw-r--r--askbot/doc/source/changelog.rst12
-rw-r--r--askbot/doc/source/contributors.rst1
-rw-r--r--askbot/forms.py104
-rw-r--r--askbot/lamson_handlers.py113
-rw-r--r--askbot/management/commands/get_tag_stats.py50
-rw-r--r--askbot/management/commands/send_email_alerts.py3
-rw-r--r--askbot/migrations/0088_install__post_view__for__development.py2
-rw-r--r--askbot/migrations/0114_auto__add_groupprofile__add_groupmembership__add_field_tag_tag_wiki.py318
-rw-r--r--askbot/migrations/0115_auto__chg_field_post_thread.py296
-rw-r--r--askbot/migrations/0116_auto__add_field_groupprofile_logo_url__add_unique_emailfeedsetting_sub.py304
-rw-r--r--askbot/migrations/0117_auto__add_field_post_approved__add_field_thread_approved__add_field_po.py335
-rw-r--r--askbot/migrations/0118_auto__add_field_postrevision_by_email.py304
-rw-r--r--askbot/migrations/0119_auto__add_postflagreason.py317
-rw-r--r--askbot/migrations/0120_auto__add_field_groupprofile_moderate_email__add_field_postrevision_em.py322
-rw-r--r--askbot/migrations/0121_auto__add_field_groupprofile_is_open__add_field_groupprofile_preapprov.py340
-rw-r--r--askbot/migrations/0122_auth_user__add_subscribed_tag_field.py318
-rw-r--r--askbot/models/__init__.py404
-rw-r--r--askbot/models/answer.py19
-rw-r--r--askbot/models/base.py6
-rw-r--r--askbot/models/meta.py81
-rw-r--r--askbot/models/post.py548
-rw-r--r--askbot/models/post_view.sql255
-rw-r--r--askbot/models/question.py103
-rw-r--r--askbot/models/reply_by_email.py42
-rw-r--r--askbot/models/repute.py80
-rw-r--r--askbot/models/tag.py60
-rw-r--r--askbot/models/user.py97
-rw-r--r--askbot/search/state_manager.py9
-rw-r--r--askbot/setup_templates/settings.py2
-rw-r--r--askbot/skins/common/media/jquery-openid/openid.css5
-rw-r--r--askbot/skins/common/media/js/live_search.js125
-rw-r--r--askbot/skins/common/media/js/post.js487
-rw-r--r--askbot/skins/common/media/js/tag_selector.js66
-rw-r--r--askbot/skins/common/media/js/user.js685
-rw-r--r--askbot/skins/common/media/js/utils.js778
-rw-r--r--askbot/skins/common/media/js/wmd/wmd.css22
-rw-r--r--askbot/skins/common/media/js/wmd/wmd.js354
-rw-r--r--askbot/skins/common/templates/authopenid/signin.html3
-rw-r--r--askbot/skins/common/templates/authopenid/signup_with_password.html6
-rw-r--r--askbot/skins/common/templates/question/answer_author_info.html4
-rw-r--r--askbot/skins/common/templates/question/question_author_info.html4
-rw-r--r--askbot/skins/common/templates/widgets/edit_post.html6
-rw-r--r--askbot/skins/common/templates/widgets/tag_selector.html33
-rw-r--r--askbot/skins/default/media/bootstrap/bootstrap.zipbin0 -> 273108 bytes
-rw-r--r--askbot/skins/default/media/bootstrap/css/bootstrap.css4647
-rw-r--r--askbot/skins/default/media/bootstrap/css/bootstrap.min.css706
-rw-r--r--askbot/skins/default/media/bootstrap/img/glyphicons-halflings-white.pngbin0 -> 4352 bytes
-rw-r--r--askbot/skins/default/media/bootstrap/img/glyphicons-halflings.pngbin0 -> 4352 bytes
-rw-r--r--askbot/skins/default/media/bootstrap/js/bootstrap.js1737
-rw-r--r--askbot/skins/default/media/bootstrap/js/bootstrap.min.js7
-rw-r--r--askbot/skins/default/media/style/lib_style.less2
-rw-r--r--askbot/skins/default/media/style/style.css663
-rw-r--r--askbot/skins/default/media/style/style.less274
-rw-r--r--askbot/skins/default/templates/base.html1
-rw-r--r--askbot/skins/default/templates/groups.html18
-rw-r--r--askbot/skins/default/templates/instant_notification.html39
-rw-r--r--askbot/skins/default/templates/instant_notification_reply_by_email.html14
-rw-r--r--askbot/skins/default/templates/macros.html108
-rw-r--r--askbot/skins/default/templates/main_page/headline.html4
-rw-r--r--askbot/skins/default/templates/main_page/javascript.html1
-rw-r--r--askbot/skins/default/templates/main_page/sidebar.html8
-rw-r--r--askbot/skins/default/templates/main_page/tag_search.html7
-rw-r--r--askbot/skins/default/templates/meta/bottom_scripts.html3
-rw-r--r--askbot/skins/default/templates/question/sidebar.html4
-rw-r--r--askbot/skins/default/templates/reopen.html3
-rw-r--r--askbot/skins/default/templates/user_profile/reject_post_dialog.html108
-rw-r--r--askbot/skins/default/templates/user_profile/user.html3
-rw-r--r--askbot/skins/default/templates/user_profile/user_inbox.html94
-rw-r--r--askbot/skins/default/templates/user_profile/user_info.html10
-rw-r--r--askbot/skins/default/templates/user_profile/user_network.html18
-rw-r--r--askbot/skins/default/templates/user_profile/user_recent.html3
-rw-r--r--askbot/skins/default/templates/user_profile/user_reputation.html3
-rw-r--r--askbot/skins/default/templates/user_profile/user_stats.html37
-rw-r--r--askbot/skins/default/templates/user_profile/user_tabs.html2
-rw-r--r--askbot/skins/default/templates/user_profile/user_votes.html3
-rw-r--r--askbot/skins/default/templates/users.html109
-rw-r--r--askbot/skins/default/templates/widgets/group_info.html92
-rw-r--r--askbot/skins/default/templates/widgets/group_snippet.html2
-rw-r--r--askbot/skins/default/templates/widgets/meta_nav.html10
-rw-r--r--askbot/skins/default/templates/widgets/question_summary.html5
-rw-r--r--askbot/skins/default/templates/widgets/user_list.html8
-rw-r--r--askbot/skins/default/templates/widgets/user_long_score_and_badge_summary.html36
-rw-r--r--askbot/skins/default/templates/widgets/user_navigation.html12
-rw-r--r--askbot/skins/default/templates/widgets/user_score_and_badge_summary.html32
-rw-r--r--askbot/skins/utils.py27
-rw-r--r--askbot/templatetags/extra_filters_jinja.py11
-rw-r--r--askbot/tests/form_tests.py1
-rw-r--r--askbot/tests/page_load_tests.py31
-rw-r--r--askbot/tests/post_model_tests.py10
-rw-r--r--askbot/tests/reply_by_email_tests.py50
-rw-r--r--askbot/urls.py83
-rw-r--r--askbot/utils/decorators.py27
-rw-r--r--askbot/utils/file_utils.py3
-rw-r--r--askbot/utils/functions.py19
-rw-r--r--askbot/utils/mail.py137
-rw-r--r--askbot/views/commands.py302
-rw-r--r--askbot/views/meta.py2
-rw-r--r--askbot/views/readers.py30
-rw-r--r--askbot/views/users.py153
-rw-r--r--askbot/views/writers.py16
-rw-r--r--askbot_requirements.txt1
-rw-r--r--askbot_requirements_dev.txt1
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('&times;');
+ 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"/>&nbsp;
<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"/>&nbsp;
+ <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
new file mode 100644
index 00000000..86a13bb9
--- /dev/null
+++ b/askbot/skins/default/media/bootstrap/bootstrap.zip
Binary files differ
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
new file mode 100644
index 00000000..a20760bf
--- /dev/null
+++ b/askbot/skins/default/media/bootstrap/img/glyphicons-halflings-white.png
Binary files differ
diff --git a/askbot/skins/default/media/bootstrap/img/glyphicons-halflings.png b/askbot/skins/default/media/bootstrap/img/glyphicons-halflings.png
new file mode 100644
index 00000000..92d4445d
--- /dev/null
+++ b/askbot/skins/default/media/bootstrap/img/glyphicons-halflings.png
Binary files differ
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">&nbsp;({{comment.added_at|diff_date}})</span>
+ <span class="age">&nbsp;({{ 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 &amp; 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 &amp; 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 &raquo;{% 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'>&#9679;</span>
- <span class="badgecount">{{user.gold}}</span>
- {% endif %}
- {% if user.silver %}
- <span class='badge2'>&#9679;</span>
- <span class="badgecount">{{user.silver}}</span>
- {% endif %}
- {% if user.bronze %}
- <span class='badge3'>&#9679;</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'>&#9679;</span>
+ <span class="badgecount">{{user.gold}}</span>
+ {% endif %}
+ {% if user.silver %}
+ <span class='badge2'>&#9679;</span>
+ <span class="badgecount">{{user.silver}}</span>
+ {% endif %}
+ {% if user.bronze %}
+ <span class='badge3'>&#9679;</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'>&#9679;</span>
- <span class="badgecount">{{user.gold}}</span>
- {% endif %}
- {% if user.silver %}
- <span class='badge2'>&#9679;</span>
- <span class="badgecount">{{user.silver}}</span>
- {% endif %}
- {% if user.bronze %}
- <span class='badge3'>&#9679;</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'>&#9679;</span>
+ <span class="badgecount">{{user.gold}}</span>
+ {% endif %}
+ {% if user.silver %}
+ <span class='badge2'>&#9679;</span>
+ <span class="badgecount">{{user.silver}}</span>
+ {% endif %}
+ {% if user.bronze %}
+ <span class='badge3'>&#9679;</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