summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/__init__.py8
-rw-r--r--askbot/api.py4
-rw-r--r--askbot/conf/__init__.py1
-rw-r--r--askbot/conf/access_control.py38
-rw-r--r--askbot/conf/email.py4
-rw-r--r--askbot/conf/user_settings.py15
-rw-r--r--askbot/conf/widgets.py99
-rw-r--r--askbot/const/__init__.py43
-rw-r--r--askbot/context.py22
-rw-r--r--askbot/db0
-rw-r--r--askbot/deps/django_authopenid/forms.py4
-rw-r--r--askbot/deps/django_authopenid/urls.py12
-rw-r--r--askbot/deps/django_authopenid/util.py7
-rw-r--r--askbot/deps/django_authopenid/views.py414
-rw-r--r--askbot/doc/source/changelog.rst15
-rw-r--r--askbot/doc/source/optional-modules.rst5
-rw-r--r--askbot/forms.py79
-rw-r--r--askbot/mail/__init__.py14
-rw-r--r--askbot/mail/lamson_handlers.py11
-rw-r--r--askbot/management/commands/askbot_add_test_content.py27
-rw-r--r--askbot/management/commands/clean_session.py52
-rw-r--r--askbot/migrations/0001_initial.py26
-rw-r--r--askbot/migrations/0133_apply_global_group_to_posts_and_users.py12
-rw-r--r--askbot/migrations/0135_auto__add_questionwidget__add_askwidget.py393
-rw-r--r--askbot/migrations/0136_auto__add_group__add_threadtogroup__add_unique_threadtogroup_thread_ta.py417
-rw-r--r--askbot/migrations/0137_create_groups_from_relevant_tags.py447
-rw-r--r--askbot/migrations/0138_auto__del_groupprofile__del_groupmembership__del_unique_groupmembershi.py441
-rw-r--r--askbot/migrations/0139_add__openness__field_to_group.py359
-rw-r--r--askbot/migrations/0140_move_group_field__is_open__to_openness.py380
-rw-r--r--askbot/migrations/0141_auto__del_field_group_is_open.py358
-rw-r--r--askbot/migrations/0142_auto__add_groupmembership.py371
-rw-r--r--askbot/migrations/0143_populate_group_memberships.py378
-rw-r--r--askbot/migrations/0144_auto__add_field_questionwidget_group__add_field_askwidget_group.py384
-rw-r--r--askbot/migrations/0145_add_moderate_answers_to_enquirers__to_askbot_group.py378
-rw-r--r--askbot/migrations/0146_auto__add_field_threadtogroup_visibility.py379
-rw-r--r--askbot/migrations/0147_auto__add_field_group_description.py380
-rw-r--r--askbot/migrations_api/__init__.py17
-rw-r--r--askbot/models/__init__.py210
-rw-r--r--askbot/models/post.py91
-rw-r--r--askbot/models/question.py192
-rw-r--r--askbot/models/tag.py73
-rw-r--r--askbot/models/user.py204
-rw-r--r--askbot/models/widgets.py38
-rw-r--r--askbot/search/postgresql/user_profile_search_08312012.plsql93
-rw-r--r--askbot/setup_templates/settings.py4
-rw-r--r--askbot/setup_templates/settings.py.mustache3
-rw-r--r--askbot/setup_templates/tinymce_sample_config.py26
-rw-r--r--askbot/skins/common/media/images/sprites.pngbin12545 -> 12940 bytes
-rw-r--r--askbot/skins/common/media/js/autocompleter.js28
-rw-r--r--askbot/skins/common/media/js/live_search_new_thread.js23
-rw-r--r--askbot/skins/common/media/js/post.js245
-rw-r--r--askbot/skins/common/media/js/user.js54
-rw-r--r--askbot/skins/common/media/js/utils.js1126
-rw-r--r--askbot/skins/common/media/js/wmd/wmd.js13
-rw-r--r--askbot/skins/common/templates/authopenid/complete.html8
-rw-r--r--askbot/skins/common/templates/authopenid/signin.html32
-rw-r--r--askbot/skins/common/templates/authopenid/signup_with_password.html9
-rw-r--r--askbot/skins/common/templates/authopenid/verify_email.html14
-rw-r--r--askbot/skins/common/templates/question/answer_controls.html28
-rw-r--r--askbot/skins/common/templates/widgets/edit_post.html2
-rw-r--r--askbot/skins/default/media/bootstrap/css/bootstrap.css2
-rw-r--r--askbot/skins/default/media/images/publish.pngbin0 -> 282 bytes
-rw-r--r--askbot/skins/default/media/images/sprites.pngbin12705 -> 12478 bytes
-rw-r--r--askbot/skins/default/media/images/sprites_source/sprites.svg160
-rw-r--r--askbot/skins/default/media/images/unpublish.pngbin0 -> 294 bytes
-rw-r--r--askbot/skins/default/media/style/style.css626
-rw-r--r--askbot/skins/default/media/style/style.less265
-rw-r--r--askbot/skins/default/templates/500.html5
-rw-r--r--askbot/skins/default/templates/answer_edit.html2
-rw-r--r--askbot/skins/default/templates/ask.html3
-rw-r--r--askbot/skins/default/templates/embed/ask_by_widget.html225
-rw-r--r--askbot/skins/default/templates/embed/ask_widget_complete.html8
-rwxr-xr-xaskbot/skins/default/templates/embed/askbot_widget.css38
-rwxr-xr-xaskbot/skins/default/templates/embed/askbot_widget.js74
-rw-r--r--askbot/skins/default/templates/embed/delete_widget.html14
-rw-r--r--askbot/skins/default/templates/embed/list_widgets.html45
-rw-r--r--askbot/skins/default/templates/embed/question_widget.html (renamed from askbot/skins/default/templates/question_widget.html)7
-rw-r--r--askbot/skins/default/templates/embed/widget_form.html23
-rw-r--r--askbot/skins/default/templates/embed/widgets.html36
-rw-r--r--askbot/skins/default/templates/groups.html20
-rw-r--r--askbot/skins/default/templates/macros.html89
-rw-r--r--askbot/skins/default/templates/meta/bottom_scripts.html9
-rw-r--r--askbot/skins/default/templates/meta/html_head_stylesheets.html7
-rw-r--r--askbot/skins/default/templates/meta/tinymce_css.html (renamed from askbot/skins/default/templates/meta/tinymce.html)2
-rw-r--r--askbot/skins/default/templates/question.html42
-rw-r--r--askbot/skins/default/templates/question/answer_card.html3
-rw-r--r--askbot/skins/default/templates/question/javascript.html7
-rw-r--r--askbot/skins/default/templates/question/new_answer_form.html1
-rw-r--r--askbot/skins/default/templates/question/sidebar.html81
-rw-r--r--askbot/skins/default/templates/question_edit.html2
-rw-r--r--askbot/skins/default/templates/user_inbox/base.html63
-rw-r--r--askbot/skins/default/templates/user_inbox/group_join_requests.html50
-rw-r--r--askbot/skins/default/templates/user_inbox/responses_and_flags.html43
-rw-r--r--askbot/skins/default/templates/user_profile/user_inbox.html107
-rw-r--r--askbot/skins/default/templates/users.html10
-rw-r--r--askbot/skins/default/templates/widget_base.html3
-rw-r--r--askbot/skins/default/templates/widgets/group_info.html55
-rw-r--r--askbot/skins/default/templates/widgets/groups_list.html4
-rw-r--r--askbot/skins/default/templates/widgets/meta_nav.html17
-rw-r--r--askbot/skins/default/templates/widgets/user_list.html8
-rw-r--r--askbot/skins/default/templates/widgets/user_navigation.html3
-rw-r--r--askbot/skins/loaders.py12
-rw-r--r--askbot/startup_procedures.py81
-rw-r--r--askbot/tests/__init__.py2
-rw-r--r--askbot/tests/badge_tests.py2
-rw-r--r--askbot/tests/db_api_tests.py86
-rw-r--r--askbot/tests/form_tests.py7
-rw-r--r--askbot/tests/misc_tests.py91
-rw-r--r--askbot/tests/page_load_tests.py125
-rw-r--r--askbot/tests/permission_assertion_tests.py8
-rw-r--r--askbot/tests/post_model_tests.py41
-rw-r--r--askbot/tests/thread_model_tests.py74
-rw-r--r--askbot/tests/user_model_tests.py6
-rw-r--r--askbot/tests/utils.py66
-rw-r--r--askbot/tests/utils_tests.py17
-rw-r--r--askbot/tests/view_context_tests.py30
-rw-r--r--askbot/tests/widget_tests.py116
-rw-r--r--askbot/urls.py114
-rw-r--r--askbot/utils/console.py8
-rw-r--r--askbot/utils/forms.py85
-rw-r--r--askbot/utils/url_utils.py33
-rw-r--r--askbot/views/commands.py313
-rw-r--r--askbot/views/context.py30
-rw-r--r--askbot/views/meta.py16
-rw-r--r--askbot/views/readers.py27
-rw-r--r--askbot/views/users.py117
-rw-r--r--askbot/views/widgets.py240
-rw-r--r--askbot/views/writers.py76
-rw-r--r--askbot_requirements.txt1
129 files changed, 11117 insertions, 1376 deletions
diff --git a/askbot/__init__.py b/askbot/__init__.py
index 59840ba8..3107c689 100644
--- a/askbot/__init__.py
+++ b/askbot/__init__.py
@@ -5,6 +5,7 @@ Functions in the askbot module perform various
basic actions on behalf of the forum application
"""
import os
+import platform
VERSION = (0, 7, 43)
@@ -30,12 +31,15 @@ REQUIREMENTS = {
'recaptcha_works': 'django-recaptcha-works',
'openid': 'python-openid',
'pystache': 'pystache==0.3.1',
- 'lamson': 'Lamson',
'pytz': 'pytz',
'tinymce': 'django-tinymce',
- 'longerusername': 'longerusername'
+ 'longerusername': 'longerusername',
+ 'bs4': 'beautifulsoup4'
}
+if platform.system() != 'Windows':
+ REQUIREMENTS['lamson'] = 'Lamson'
+
#necessary for interoperability of django and coffin
try:
from askbot import patches
diff --git a/askbot/api.py b/askbot/api.py
index 57d5c1aa..9f37995e 100644
--- a/askbot/api.py
+++ b/askbot/api.py
@@ -9,7 +9,7 @@ from askbot import models
from askbot import const
def get_info_on_moderation_items(user):
- """returns a dictionary with
+ """returns a dictionary with
counts of new and seen moderation items for a given user
if user is not a moderator or admin, returns None
"""
@@ -48,7 +48,7 @@ def get_admin(seed_user_id = None):
if the user is not found, or there are no moderators/admins
User.DoesNotExist will be raised
-
+
The reason this function is here and not on a manager of
the user object is because we still patch the django-auth User table
and it's probably better not to patch the manager
diff --git a/askbot/conf/__init__.py b/askbot/conf/__init__.py
index 7ccd19bd..3e80877c 100644
--- a/askbot/conf/__init__.py
+++ b/askbot/conf/__init__.py
@@ -26,7 +26,6 @@ import askbot.conf.badges
import askbot.conf.login_providers
import askbot.conf.access_control
import askbot.conf.site_modes
-import askbot.conf.widgets
#import main settings object
from askbot.conf.settings_wrapper import settings
diff --git a/askbot/conf/access_control.py b/askbot/conf/access_control.py
index cd2364b5..5da88936 100644
--- a/askbot/conf/access_control.py
+++ b/askbot/conf/access_control.py
@@ -13,9 +13,45 @@ settings.register(
livesettings.BooleanValue(
ACCESS_CONTROL,
'ASKBOT_CLOSED_FORUM_MODE',
- default = False,
+ default=False,
description=_('Allow only registered user to access the forum'),
)
)
+EMAIL_VALIDATION_CASE_CHOICES = (
+ ('nothing', _('nothing - not required')),
+ ('see-content', _('access to content')),
+ #'post-content', _('posting content'),
+)
+
+settings.register(
+ livesettings.StringValue(
+ ACCESS_CONTROL,
+ 'REQUIRE_VALID_EMAIL_FOR',
+ default='nothing',
+ choices=EMAIL_VALIDATION_CASE_CHOICES,
+ description=_(
+ 'Require valid email for'
+ )
+ )
+)
+settings.register(
+ livesettings.LongStringValue(
+ ACCESS_CONTROL,
+ 'ALLOWED_EMAILS',
+ default='',
+ description=_('Allowed email addresses'),
+ help_text=_('Please use space to separate the entries')
+ )
+)
+
+settings.register(
+ livesettings.LongStringValue(
+ ACCESS_CONTROL,
+ 'ALLOWED_EMAIL_DOMAINS',
+ default='',
+ description=_('Allowed email domain names'),
+ help_text=_('Please use space to separate the entries, do not use the @ symbol!')
+ )
+)
diff --git a/askbot/conf/email.py b/askbot/conf/email.py
index c6c1cb1c..9330e638 100644
--- a/askbot/conf/email.py
+++ b/askbot/conf/email.py
@@ -80,7 +80,7 @@ settings.register(
livesettings.StringValue(
EMAIL,
'DEFAULT_NOTIFICATION_DELIVERY_SCHEDULE_Q_ANS',
- default='w',
+ default='d',
choices=const.NOTIFICATION_DELIVERY_SCHEDULE_CHOICES,
description=_('Default notification frequency questions answered by the user'),
help_text=_(
@@ -109,7 +109,7 @@ settings.register(
livesettings.StringValue(
EMAIL,
'DEFAULT_NOTIFICATION_DELIVERY_SCHEDULE_M_AND_C',
- default='w',
+ default='i',
choices=const.NOTIFICATION_DELIVERY_SCHEDULE_CHOICES,
description=_('Default notification frequency for mentions \
and comments'),
diff --git a/askbot/conf/user_settings.py b/askbot/conf/user_settings.py
index 84c16628..adbdd5ff 100644
--- a/askbot/conf/user_settings.py
+++ b/askbot/conf/user_settings.py
@@ -16,11 +16,20 @@ USER_SETTINGS = livesettings.ConfigurationGroup(
)
settings.register(
- livesettings.StringValue(
+ livesettings.LongStringValue(
USER_SETTINGS,
'NEW_USER_GREETING',
- default = '',
- description = _('On-screen greeting shown to the new users')
+ default='',
+ description=_('On-screen greeting shown to the new users')
+ )
+)
+
+settings.register(
+ livesettings.BooleanValue(
+ USER_SETTINGS,
+ 'ALLOW_ANONYMOUS_FEEDBACK',
+ default=True,
+ description=_('Allow anonymous users send feedback')
)
)
diff --git a/askbot/conf/widgets.py b/askbot/conf/widgets.py
deleted file mode 100644
index d704ea12..00000000
--- a/askbot/conf/widgets.py
+++ /dev/null
@@ -1,99 +0,0 @@
-"""
-Settings for embeddable widgets
-"""
-from django.utils.translation import ugettext as _
-from django.utils.html import escape
-from askbot.conf.settings_wrapper import settings
-from askbot.deps.livesettings import ConfigurationGroup
-from askbot.deps.livesettings import values
-from askbot.conf.super_groups import CONTENT_AND_UI
-
-EMBEDDABLE_WIDGETS = ConfigurationGroup(
- 'EMBEDDABLE_WIDGETS',
- _('Embeddable widgets'),
- super_group = CONTENT_AND_UI
-)
-
-#we need better capabilities for the settings here
-#
-
-settings.register(
- values.IntegerValue(
- EMBEDDABLE_WIDGETS,
- 'QUESTIONS_WIDGET_MAX_QUESTIONS',
- default = 7,
- description = _('Number of questions to show'),
- help_text = escape(
- _(
- 'To embed the widget, add the following code '
- 'to your site (and fill in correct base url, preferred tags, width and height):'
- '<iframe '
- 'src="{{base_url}}/widgets/questions?tags={{comma-separated-tags}}" '
- 'width="100%" '
- 'height="300"'
- 'scrolling="no">'
- '<p>Your browser does not support iframes.</p>'
- '</iframe>'
- )
- )
- )
-)
-settings.register(
- values.LongStringValue(
- EMBEDDABLE_WIDGETS,
- 'QUESTIONS_WIDGET_CSS',
- default = """
-body {
- overflow: hidden;
-}
-#container {
- width: 200px;
- height: 350px;
-}
-ul {
- list-style: none;
- padding: 5px;
- margin: 5px;
-}
-li {
- border-bottom: #CCC 1px solid;
- padding-bottom: 5px;
- padding-top: 5px;
-}
-li:last-child {
- border: none;
-}
-a {
- text-decoration: none;
- color: #464646;
- font-family: 'Yanone Kaffeesatz', sans-serif;
- font-size: 15px;
-}
-""",
- descripton = _('CSS for the questions widget')
- )
-)
-
-settings.register(
- values.LongStringValue(
- EMBEDDABLE_WIDGETS,
- 'QUESTIONS_WIDGET_HEADER',
- description = _('Header for the questions widget'),
- default = ''
- )
-)
-
-settings.register(
- values.LongStringValue(
- EMBEDDABLE_WIDGETS,
- 'QUESTIONS_WIDGET_FOOTER',
- description = _('Footer for the questions widget'),
- default = """
-<link
- href='http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700'
- rel='stylesheet'
- type='text/css'
->
-"""
- )
-)
diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py
index dfb6995a..5f47bb79 100644
--- a/askbot/const/__init__.py
+++ b/askbot/const/__init__.py
@@ -177,6 +177,7 @@ TYPE_ACTIVITY_CREATE_REJECT_REASON = 26
TYPE_ACTIVITY_UPDATE_REJECT_REASON = 27
TYPE_ACTIVITY_VALIDATION_EMAIL_SENT = 28
TYPE_ACTIVITY_POST_SHARED = 29
+TYPE_ACTIVITY_ASK_TO_JOIN_GROUP = 30
#TYPE_ACTIVITY_EDIT_QUESTION = 17
#TYPE_ACTIVITY_EDIT_ANSWER = 18
@@ -403,5 +404,47 @@ AVATAR_STATUS_CHOICE = (
('a', _('Uploaded Avatar')),#avatar uploaded locally - with django-avatar app
)
+SEARCH_ORDER_BY = (
+ ('-added_at', _('date descendant')),
+ ('added_at', _('date ascendant')),
+ ('-last_activity_at', _('activity descendant')),
+ ('last_activity_at', _('activity ascendant')),
+ ('-answer_count', _('answers descendant')),
+ ('answer_count', _('answers ascendant')),
+ ('-score', _('votes descendant')),
+ ('score', _('votes ascendant')),
+ )
+
+DEFAULT_QUESTION_WIDGET_STYLE = """
+@import url('http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700');
+body {
+ overflow: hidden;
+}
+
+#container {
+ width: 200px;
+ height: 350px;
+}
+ul {
+ list-style: none;
+ padding: 5px;
+ margin: 5px;
+}
+li {
+ border-bottom: #CCC 1px solid;
+ padding-bottom: 5px;
+ padding-top: 5px;
+}
+li:last-child {
+ border: none;
+}
+a {
+ text-decoration: none;
+ color: #464646;
+ font-family: 'Yanone Kaffeesatz', sans-serif;
+ font-size: 15px;
+}
+"""
+
#an exception import * because that file has only strings
from askbot.const.message_keys import *
diff --git a/askbot/context.py b/askbot/context.py
index 3422c701..2dace3eb 100644
--- a/askbot/context.py
+++ b/askbot/context.py
@@ -4,12 +4,17 @@ and the application available for the templates
"""
import sys
from django.conf import settings
+from django.core.urlresolvers import reverse
+from django.utils import simplejson
+
import askbot
from askbot import api
+from askbot import models
from askbot import const
from askbot.conf import settings as askbot_settings
from askbot.skins.loaders import get_skin
from askbot.utils import url_utils
+from askbot.utils.slug import slugify
def application_settings(request):
"""The context processor function"""
@@ -46,9 +51,24 @@ def application_settings(request):
my_settings['LOGOUT_REDIRECT_URL'] = url_utils.get_logout_redirect_url()
my_settings['USE_ASKBOT_LOGIN_SYSTEM'] = 'askbot.deps.django_authopenid' \
in settings.INSTALLED_APPS
- return {
+ context = {
'settings': my_settings,
'skin': get_skin(request),
'moderation_items': api.get_info_on_moderation_items(request.user),
'noscript_url': const.DEPENDENCY_URLS['noscript'],
}
+
+ if askbot_settings.GROUPS_ENABLED:
+ groups = models.Group.objects.exclude(
+ name__startswith='_internal_'
+ ).values('id', 'name')
+ group_list = []
+ for group in groups:
+ group_slug = slugify(group['name'])
+ link = reverse('users_by_group',
+ kwargs={'group_id': group['id'],
+ 'group_slug': group_slug})
+ group_list.append({'name': group['name'], 'link': link})
+ context['group_list'] = simplejson.dumps(group_list)
+
+ return context
diff --git a/askbot/db b/askbot/db
deleted file mode 100644
index e69de29b..00000000
--- a/askbot/db
+++ /dev/null
diff --git a/askbot/deps/django_authopenid/forms.py b/askbot/deps/django_authopenid/forms.py
index db2afcd8..fbc5c6ff 100644
--- a/askbot/deps/django_authopenid/forms.py
+++ b/askbot/deps/django_authopenid/forms.py
@@ -308,14 +308,14 @@ class LoginForm(forms.Form):
class OpenidRegisterForm(forms.Form):
""" openid signin form """
next = NextUrlField()
- username = UserNameField()
+ username = UserNameField(widget_attrs={'tabindex': 0})
email = UserEmailField()
class ClassicRegisterForm(SetPasswordForm):
""" legacy registration form """
next = NextUrlField()
- username = UserNameField()
+ username = UserNameField(widget_attrs={'tabindex': 0})
email = UserEmailField()
login_provider = PasswordLoginProviderField()
#fields password1 and password2 are inherited
diff --git a/askbot/deps/django_authopenid/urls.py b/askbot/deps/django_authopenid/urls.py
index 9b97d847..1b7d0b01 100644
--- a/askbot/deps/django_authopenid/urls.py
+++ b/askbot/deps/django_authopenid/urls.py
@@ -21,11 +21,6 @@ urlpatterns = patterns('askbot.deps.django_authopenid.views',
),
url(r'^%s$' % _('register/'), 'register', name='user_register'),
url(
- r'^%s$' % _('verify-user-information/'),
- 'verify_user_information',
- name = 'verify_user_information'
- ),
- url(
r'^%s$' % _('signup/'),
'signup_with_password',
name='user_signup_with_password'
@@ -35,7 +30,12 @@ urlpatterns = patterns('askbot.deps.django_authopenid.views',
#but the setting is disabled right now
#url(r'^%s%s$' % (_('email/'), _('sendkey/')), 'send_email_key', name='send_email_key'),
#url(r'^%s%s(?P<id>\d+)/(?P<key>[\dabcdef]{32})/$' % (_('email/'), _('verify/')), 'verifyemail', name='user_verifyemail'),
- url(r'^%s(?P<key>[\dabcdef]{32})?$' % _('recover/'), 'account_recover', name='user_account_recover'),
+ url(r'^%s$' % _('recover/'), 'account_recover', name='user_account_recover'),
+ url(
+ r'^%s$' % _('verify-email/'),
+ 'verify_email_and_register',
+ name='verify_email_and_register'
+ ),
url(
r'^delete_login_method/$',#this method is ajax only
'delete_login_method',
diff --git a/askbot/deps/django_authopenid/util.py b/askbot/deps/django_authopenid/util.py
index 28f6b2dd..9f02050d 100644
--- a/askbot/deps/django_authopenid/util.py
+++ b/askbot/deps/django_authopenid/util.py
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
import cgi
import urllib
+import urllib2
import functools
import re
+import random
from openid.store.interface import OpenIDStore
from openid.association import Association as OIDAssociation
from openid.extensions import sreg
@@ -412,6 +414,7 @@ def get_enabled_major_login_providers():
token = oauth.Token(data['oauth_token'], data['oauth_token_secret'])
client = oauth.Client(consumer, token=token)
url = 'https://identi.ca/api/account/verify_credentials.json'
+ content = urllib2.urlopen(url).read()
json = simplejson.loads(content)
return json['id']
if askbot_settings.IDENTICA_KEY and askbot_settings.IDENTICA_SECRET:
@@ -848,3 +851,7 @@ def ldap_check_password(username, password):
except ldap.LDAPError, e:
logging.critical(unicode(e))
return False
+
+def generate_random_key():
+ random.seed()
+ return '%032x' % random.getrandbits(128)
diff --git a/askbot/deps/django_authopenid/views.py b/askbot/deps/django_authopenid/views.py
index 73280011..5498c792 100644
--- a/askbot/deps/django_authopenid/views.py
+++ b/askbot/deps/django_authopenid/views.py
@@ -84,15 +84,70 @@ from askbot.utils.forms import get_next_url
from askbot.utils.http import get_request_info
from askbot.models.signals import user_logged_in, user_registered
+def create_authenticated_user_account(
+ username=None, email=None, password=None,
+ user_identifier=None, login_provider_name=None
+):
+ """creates a user account, user association with
+ the login method and the the default email subscriptions
+ """
+
+ user = User.objects.create_user(username, email)
+ user_registered.send(None, user=user)
+
+ logging.debug('creating new openid user association for %s')
+
+ if password:
+ user.set_password(password)
+ user.save()
+ else:
+ UserAssociation(
+ openid_url = user_identifier,
+ user = user,
+ provider_name = login_provider_name,
+ last_used_timestamp = datetime.datetime.now()
+ ).save()
+
+ subscribe_form = askbot_forms.SimpleEmailSubscribeForm({'subscribe': 'y'})
+ subscribe_form.full_clean()
+ logging.debug('saving email feed settings')
+ subscribe_form.save(user)
+
+ logging.debug('logging the user in')
+ user = authenticate(method='force', user_id=user.id)
+ if user is None:
+ error_message = 'please make sure that ' + \
+ 'askbot.deps.django_authopenid.backends.AuthBackend' + \
+ 'is in your settings.AUTHENTICATION_BACKENDS'
+ raise Exception(error_message)
+
+ return user
+
+
+def cleanup_post_register_session(request):
+ """delete keys from session after registration is complete"""
+ keys = (
+ 'user_identifier',
+ 'login_provider_name',
+ 'username',
+ 'email',
+ 'password',
+ 'validation_code'
+ )
+ for key in keys:
+ if key in request.session:
+ del request.session[key]
+
+
#todo: decouple from askbot
-def login(request,user):
+def login(request, user):
from django.contrib.auth import login as _login
# get old session key
session_key = request.session.session_key
# login and get new session key
- _login(request,user)
+ _login(request, user)
# send signal with old session key as argument
logging.debug('logged in user %s with session key %s' % (user.username, session_key))
@@ -282,7 +337,6 @@ def signin(request, template_name='authopenid/signin.html'):
"""
logging.debug('in signin view')
on_failure = signin_failure
- email_feeds_form = askbot_forms.SimpleEmailSubscribeForm()
#we need a special priority on where to redirect on successful login
#here:
@@ -333,6 +387,8 @@ def signin(request, template_name='authopenid/signin.html'):
if askbot_settings.LDAP_AUTOCREATE_USERS:
#create new user or
user = ldap_create_user(user_info).user
+ user = authenticate(method='force', user_id=user.id)
+ assert(user is not None)
login(request, user)
return HttpResponseRedirect(next_url)
else:
@@ -799,53 +855,6 @@ def finalize_generic_signin(
user_identifier=user_identifier
)
-@login_required
-@csrf.csrf_protect
-def verify_user_information(request):
- """this view collects the same information from
- user ase :func:`register`, but requires that user is
- already logged in and does not create a new user record
- or change anything in instances of :class:`UserAssociation`
- """
- register_form = forms.OpenidRegisterForm(
- initial={
- 'next': reverse('index'),
- 'username': request.user.username,
- 'email': request.user.email
- }
- )
- email_feeds_form = askbot_forms.SimpleEmailSubscribeForm()
-
- if request.method == 'POST':
- register_form = forms.OpenidRegisterForm(request.POST)
- email_feeds_form = askbot_forms.SimpleEmailSubscribeForm(request.POST)
- if register_form.is_valid() and email_feeds_form.is_valid():
-
- email_feeds_form.save(request.user)
-
- request.user.username = register_form.cleaned_data['username']
- request.user.email = register_form.cleaned_data['email']
- request.user.save()
-
- if askbot_settings.EMAIL_VALIDATION == True:
- logging.debug('sending email validation')
- send_new_email_key(request.user, nomessage=True)
- output = validation_email_sent(request)
- set_email_validation_message(request.user) #message set after generating view
- return output
-
- logging.debug('success, send user to main page')
- return HttpResponseRedirect(reverse('index'))
-
- logging.debug('printing authopenid/complete.html output')
- data = {
- 'openid_register_form': register_form,
- 'email_feeds_form': email_feeds_form,
- 'default_form_action': request.path #post to this view
- }
- return render_into_skin('authopenid/complete.html', data, request)
-
-
@not_authenticated
@csrf.csrf_protect
def register(request, login_provider_name=None, user_identifier=None):
@@ -868,7 +877,6 @@ def register(request, login_provider_name=None, user_identifier=None):
next_url = get_next_url(request)
user = None
- is_redirect = False
username = request.session.get('username', '')
email = request.session.get('email', '')
logging.debug('request method is %s' % request.method)
@@ -880,7 +888,6 @@ def register(request, login_provider_name=None, user_identifier=None):
'email': request.session.get('email', ''),
}
)
- email_feeds_form = askbot_forms.SimpleEmailSubscribeForm()
if request.method == 'GET':
assert(login_provider_name is not None)
@@ -903,14 +910,9 @@ def register(request, login_provider_name=None, user_identifier=None):
logging.debug('trying to create new account associated with openid')
register_form = forms.OpenidRegisterForm(request.POST)
- email_feeds_form = askbot_forms.SimpleEmailSubscribeForm(request.POST)
if not register_form.is_valid():
logging.debug('OpenidRegisterForm is INVALID')
- elif not email_feeds_form.is_valid():
- logging.debug('SimpleEmailSubscribeForm is INVALID')
else:
- logging.debug('OpenidRegisterForm and SimpleEmailSubscribeForm are valid')
- is_redirect = True
username = register_form.cleaned_data['username']
email = register_form.cleaned_data['email']
@@ -922,60 +924,30 @@ def register(request, login_provider_name=None, user_identifier=None):
user_info['email'] = email
user = ldap_create_user(user_info).user
del request.session['ldap_user_info']
- else:
- user = User()
- user.username = username
- user.email = email
- #todo - maybe hide these names per some option
- #user.first_name = request.session.get('first_name', '')
- #user.last_name = request.session.get('last_name', '')
- user.save()
-
- user_registered.send(None, user = user)
-
- logging.debug('creating new openid user association for %s')
-
- UserAssociation(
- openid_url = user_identifier,
- user = user,
- provider_name = login_provider_name,
- last_used_timestamp = datetime.datetime.now()
- ).save()
-
- del request.session['user_identifier']
- del request.session['login_provider_name']
-
- logging.debug('logging the user in')
-
- user = authenticate(method = 'force', user_id = user.id)
- if user is None:
- error_message = 'please make sure that ' + \
- 'askbot.deps.django_authopenid.backends.AuthBackend' + \
- 'is in your settings.AUTHENTICATION_BACKENDS'
- raise Exception(error_message)
+ login(request, user)
+ cleanup_post_register_session(request)
+ return HttpResponseRedirect(next_url)
- login(request, user)
+ elif askbot_settings.REQUIRE_VALID_EMAIL_FOR == 'nothing':
- logging.debug('saving email feed settings')
- email_feeds_form.save(user)
-
- #check if we need to post a question that was added anonymously
- #this needs to be a function call becase this is also done
- #if user just logged in and did not need to create the new account
-
- if user != None:
- if askbot_settings.EMAIL_VALIDATION == True:
- logging.debug('sending email validation')
- send_new_email_key(user, nomessage=True)
- output = validation_email_sent(request)
- set_email_validation_message(user) #message set after generating view
- return output
- if user.is_authenticated():
- logging.debug('success, send user to main page')
- return HttpResponseRedirect(reverse('index'))
+ user = create_authenticated_user_account(
+ username=username,
+ email=email,
+ user_identifier=user_identifier,
+ login_provider_name=login_provider_name,
+ )
+ login(request, user)
+ cleanup_post_register_session(request)
+ return HttpResponseRedirect(next_url)
else:
- logging.debug('have really strange error')
- raise Exception('openid login failed')#should not ever get here
+ request.session['username'] = username
+ request.session['email'] = email
+ key = util.generate_random_key()
+ email = request.session['email']
+ send_email_key(email, key, handler_url_name='verify_email_and_register')
+ request.session['validation_code'] = key
+ redirect_url = reverse('verify_email_and_register') + '?next=' + next_url
+ return HttpResponseRedirect(redirect_url)
providers = {
'yahoo':'<font color="purple">Yahoo!</font>',
@@ -993,7 +965,6 @@ def register(request, login_provider_name=None, user_identifier=None):
logging.debug('printing authopenid/complete.html output')
data = {
'openid_register_form': register_form,
- 'email_feeds_form': email_feeds_form,
'default_form_action': django_settings.LOGIN_URL,
'provider':mark_safe(provider_logo),
'username': username,
@@ -1011,6 +982,58 @@ def signin_failure(request, message):
return show_signin_view(request)
@not_authenticated
+@csrf.csrf_protect
+def verify_email_and_register(request):
+ """for POST request - check the validation code,
+ and if correct - create an account an log in the user
+
+ for GET - give a field to paste the activation code
+ and a button to send another validation email.
+ """
+ presented_code = request.REQUEST.get('validation_code', None)
+ if presented_code:
+ try:
+ #we get here with post if button is pushed
+ #or with "get" if emailed link is clicked
+ expected_code = request.session['validation_code']
+ assert(presented_code == expected_code)
+ #create an account!
+ username = request.session['username']
+ email = request.session['email']
+ password = request.session.get('password', None)
+ user_identifier = request.session.get('user_identifier', None)
+ login_provider_name = request.session.get('login_provider_name', None)
+ if password:
+ user = create_authenticated_user_account(
+ username=username,
+ email=email,
+ password=password,
+ )
+ elif user_identifier and login_provider_name:
+ user = create_authenticated_user_account(
+ username=username,
+ email=email,
+ user_identifier=user_identifier,
+ login_provider_name=login_provider_name,
+ )
+ else:
+ raise NotImplementedError()
+
+ login(request, user)
+ cleanup_post_register_session(request)
+ return HttpResponseRedirect(get_next_url(request))
+ except Exception, e:
+ message = _(
+ 'Sorry, registration failed. '
+ 'Please ask the site administrator for help.'
+ )
+ request.user.message_set.create(message=message)
+ return HttpResponseRedirect(reverse('index'))
+ else:
+ data = {'page_class': 'validate-email-page'}
+ return render_into_skin('authopenid/verify_email.html', data, request)
+
+@not_authenticated
@decorators.valid_password_login_provider_required
@csrf.csrf_protect
@fix_recaptcha_remote_ip
@@ -1020,8 +1043,7 @@ def signup_with_password(request):
"""
logging.debug(get_request_info(request))
- next = get_next_url(request)
- login_form = forms.LoginForm(initial = {'next': next})
+ login_form = forms.LoginForm(initial = {'next': get_next_url(request)})
#this is safe because second decorator cleans this field
provider_name = request.REQUEST['login_provider']
@@ -1033,7 +1055,6 @@ def signup_with_password(request):
logging.debug('request method was %s' % request.method)
if request.method == 'POST':
form = RegisterForm(request.POST)
- email_feeds_form = askbot_forms.SimpleEmailSubscribeForm(request.POST)
#validation outside if to remember form values
logging.debug('validating classic register form')
@@ -1042,53 +1063,36 @@ def signup_with_password(request):
logging.debug('classic register form validated')
else:
logging.debug('classic register form is not valid')
- form2_is_valid = email_feeds_form.is_valid()
- if form2_is_valid:
- logging.debug('email feeds form validated')
- else:
- logging.debug('email feeds form is not valid')
- if form1_is_valid and form2_is_valid:
+
+ if form1_is_valid:
logging.debug('both forms are valid')
next = form.cleaned_data['next']
username = form.cleaned_data['username']
password = form.cleaned_data['password1']
email = form.cleaned_data['email']
- provider_name = form.cleaned_data['login_provider']
-
- new_user = User.objects.create_user(username, email, password)
- user_registered.send(None, user = new_user)
-
- logging.debug('new user %s created' % username)
- if provider_name != 'local':
- raise NotImplementedError('must run create external user code')
- user = authenticate(
- username = username,
- password = password,
- provider_name = provider_name,
- method = 'password'
- )
+ if askbot_settings.REQUIRE_VALID_EMAIL_FOR == 'nothing':
+ user = create_authenticated_user_account(
+ username=username,
+ email=email,
+ password=password,
+ )
+ login(request, user)
+ cleanup_post_register_session(request)
+ return HttpResponseRedirect(get_next_url(request))
+ else:
+ request.session['username'] = username
+ request.session['email'] = email
+ request.session['password'] = password
+ #todo: generate a key and save it in the session
+ key = util.generate_random_key()
+ email = request.session['email']
+ send_email_key(email, key, handler_url_name='verify_email_and_register')
+ request.session['validation_code'] = key
+ redirect_url = reverse('verify_email_and_register') + \
+ '?next=' + get_next_url(request)
+ return HttpResponseRedirect(redirect_url)
- login(request, user)
- logging.debug('new user logged in')
- email_feeds_form.save(user)
- logging.debug('email feeds form saved')
-
- # send email
- #subject = _("Welcome email subject line")
- #message_template = get_emplate(
- # 'authopenid/confirm_email.txt'
- #)
- #message_context = Context({
- # 'signup_url': askbot_settings.APP_URL + reverse('user_signin'),
- # 'username': username,
- # 'password': password,
- #})
- #message = message_template.render(message_context)
- #send_mail(subject, message, django_settings.DEFAULT_FROM_EMAIL,
- # [user.email])
- #logging.debug('new password acct created, confirmation email sent!')
- return HttpResponseRedirect(next)
else:
#todo: this can be solved with a decorator, maybe
form.initial['login_provider'] = provider_name
@@ -1097,11 +1101,10 @@ def signup_with_password(request):
#todo: here we have duplication of get_password_login_provider...
form = RegisterForm(
initial={
- 'next':next,
+ 'next': get_next_url(request),
'login_provider': provider_name
}
)
- email_feeds_form = askbot_forms.SimpleEmailSubscribeForm()
logging.debug('printing legacy signup form')
major_login_providers = util.get_enabled_major_login_providers()
@@ -1110,7 +1113,6 @@ def signup_with_password(request):
context_data = {
'form': form,
'page_class': 'openid-signin',
- 'email_feeds_form': email_feeds_form,
'major_login_providers': major_login_providers.values(),
'minor_login_providers': minor_login_providers.values(),
'login_form': login_form
@@ -1158,89 +1160,35 @@ def xrdf(request):
return_to = "%s%s" % (url_host, reverse('user_complete_signin'))
return HttpResponse(XRDF_TEMPLATE % {'return_to': return_to})
-def find_email_validation_messages(user):
- msg_text = _('your email needs to be validated see %(details_url)s') \
- % {'details_url':reverse('faq') + '#validate'}
- return user.message_set.filter(message__exact=msg_text)
-
-def set_email_validation_message(user):
- messages = find_email_validation_messages(user)
- msg_text = _('your email needs to be validated see %(details_url)s') \
- % {'details_url':reverse('faq') + '#validate'}
- if len(messages) == 0:
- user.message_set.create(message=msg_text)
-
-def clear_email_validation_message(user):
- messages = find_email_validation_messages(user)
- messages.delete()
-
-def set_new_email(user, new_email, nomessage=False):
+def set_new_email(user, new_email):
if new_email != user.email:
user.email = new_email
user.email_isvalid = False
user.save()
- if askbot_settings.EMAIL_VALIDATION == True:
- send_new_email_key(user,nomessage=nomessage)
-def _send_email_key(user):
+def send_email_key(email, key, handler_url_name='user_account_recover'):
"""private function. sends email containing validation key
to user's email address
"""
- subject = _("Recover your %(site)s account") % {'site': askbot_settings.APP_SHORT_NAME}
+ subject = _("Recover your %(site)s account") % \
+ {'site': askbot_settings.APP_SHORT_NAME}
url = urlparse(askbot_settings.APP_URL)
data = {
'validation_link': url.scheme + '://' + url.netloc + \
- reverse(
- 'user_account_recover',
- kwargs={'key':user.email_key}
- )
+ reverse(handler_url_name) +\
+ '?validation_code=' + key
}
template = get_template('authopenid/email_validation.txt')
message = template.render(data)
- send_mail(subject, message, django_settings.DEFAULT_FROM_EMAIL, [user.email])
+ send_mail(subject, message, django_settings.DEFAULT_FROM_EMAIL, [email])
-def send_new_email_key(user,nomessage=False):
- import random
- random.seed()
- user.email_key = '%032x' % random.getrandbits(128)
+def send_user_new_email_key(user):
+ user.email_key = util.generate_random_key()
user.save()
- _send_email_key(user)
- if nomessage==False:
- set_email_validation_message(user)
-
-@login_required
-@csrf.csrf_protect
-def send_email_key(request):
- """
- url = /email/sendkey/
-
- view that is shown right after sending email key
- email sending is called internally
+ send_email_key(user.email, user.email_key)
- raises 404 if email validation is off
- if current email is valid shows 'key_not_sent' view of
- authopenid/changeemail.html template
- """
- if askbot_settings.EMAIL_VALIDATION == True:
- if request.user.email_isvalid:
- data = {
- 'email': request.user.email,
- 'action_type': 'key_not_sent',
- 'change_link': reverse('user_changeemail')
- }
- return render_into_skin(
- 'authopenid/changeemail.html',
- data,
- request
- )
- else:
- send_new_email_key(request.user)
- return validation_email_sent(request)
- else:
- raise Http404
-
-def account_recover(request, key = None):
+def account_recover(request):
"""view similar to send_email_key, except
it allows user to recover an account by entering
his/her email address
@@ -1256,7 +1204,7 @@ def account_recover(request, key = None):
form = forms.AccountRecoveryForm(request.POST)
if form.is_valid():
user = form.cleaned_data['user']
- send_new_email_key(user, nomessage = True)
+ send_user_new_email_key(user)
message = _(
'Please check your email and visit the enclosed link.'
)
@@ -1271,6 +1219,7 @@ def account_recover(request, key = None):
account_recovery_form = form
)
else:
+ key = request.GET.get('validation_code', None)
if key is None:
return HttpResponseRedirect(reverse('user_signin'))
@@ -1304,26 +1253,3 @@ def validation_email_sent(request):
'action_type': 'validate'
}
return render_into_skin('authopenid/changeemail.html', data, request)
-
-def verifyemail(request,id=None,key=None):
- """
- view that is shown when user clicks email validation link
- url = /email/verify/{{user.id}}/{{user.email_key}}/
- """
- logging.debug('')
- if askbot_settings.EMAIL_VALIDATION == True:
- user = User.objects.get(id=id)
- if user:
- if user.email_key == key:
- user.email_isvalid = True
- clear_email_validation_message(user)
- user.save()
- data = {'action_type': 'validation_complete'}
- return render_into_skin(
- 'authopenid/changeemail.html',
- data,
- request
- )
- else:
- logging.error('hmm, no user found for email validation message - foul play?')
- raise Http404
diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst
index b312d2f7..88fc90d8 100644
--- a/askbot/doc/source/changelog.rst
+++ b/askbot/doc/source/changelog.rst
@@ -3,11 +3,24 @@ Changes in Askbot
Development version
-------------------
- adding "extra options" to the ldap session (Evgeny)
+* Repost comment as answer (Adolfo)
+* Question list widget (Adolfo)
+* Ask a question widget (Adolfo)
+* Embeddable widget generator (Adolfo)
+* Groups are shown in the dropdown menu in the header (Adolfo)
+* Added group moderation requests to the moderators inboxes (Evgeny)
+* Group joining may be open/closed or moderated (Evgeny)
+* Adding "extra options" to the ldap session (Evgeny)
* Tag moderation (Evgeny)
* Editable optional three level category selector for the tags (Evgeny)
* Tag editor adding tags as they are typed (Evgeny)
* Added optional support for unicode slugs (Evgeny)
+* Allow switching comment with answer and answer with question comment (Adolfo)
+* Allow user names longer than 30 characters (Evgeny)
+* Option to disable feedback form for the anonymos users (Evgeny)
+* Optional restriction to have confirmed email address to join forum (Evgeny)
+* Optional list of allowed email addresses and email domain name for the new users (Evgeny)
+* Optional support for unicode slugs (Evgeny)
* Optionally allow limiting one answer per question per person (Evgeny)
* Added management command `build_livesettings_cache` (Adolfo)
* Administrators can post under fictional user accounts without logging out (jtrain, Evgeny)
diff --git a/askbot/doc/source/optional-modules.rst b/askbot/doc/source/optional-modules.rst
index c8f2dba3..4ba7b925 100644
--- a/askbot/doc/source/optional-modules.rst
+++ b/askbot/doc/source/optional-modules.rst
@@ -276,6 +276,11 @@ Askbot supports posting replies by email. For this feature to work ``Lamson`` a
pip install django-lamson
+.. note::
+ On Windows installation of the Lamson module may require
+ additional work. Askbot does not support this feature
+ on Windows automatically.
+
The lamson daemon needs a folder to store it's mail queue files and a folder to store log files, create the folders folder named ``run`` and ``logs`` within your project folder by executing the following commands:
mkdir run
diff --git a/askbot/forms.py b/askbot/forms.py
index 7030643a..eb791595 100644
--- a/askbot/forms.py
+++ b/askbot/forms.py
@@ -12,6 +12,7 @@ from django.contrib.auth.models import User
from django_countries import countries
from askbot.utils.forms import NextUrlField, UserNameField
from askbot.mail import extract_first_email_address
+from askbot.models.tag import get_groups
from recaptcha_works.fields import RecaptchaField
from askbot.conf import settings as askbot_settings
from askbot.conf import get_tag_display_filter_strategy_choices
@@ -273,13 +274,14 @@ class EditorField(forms.CharField):
min_length = 10 # sentinel default value
def __init__(self, *args, **kwargs):
+ editor_attrs = kwargs.pop('editor_attrs', {})
super(EditorField, self).__init__(*args, **kwargs)
self.required = True
widget_attrs = {'id': 'editor'}
if askbot_settings.EDITOR_TYPE == 'markdown':
self.widget = forms.Textarea(attrs=widget_attrs)
elif askbot_settings.EDITOR_TYPE == 'tinymce':
- self.widget = TinyMCE(attrs=widget_attrs)
+ self.widget = TinyMCE(attrs=widget_attrs, mce_attrs=editor_attrs)
self.label = _('content')
self.help_text = u''
self.initial = ''
@@ -357,7 +359,8 @@ class TagNamesField(forms.CharField):
def __init__(self, *args, **kwargs):
super(TagNamesField, self).__init__(*args, **kwargs)
- self.required = askbot_settings.TAGS_ARE_REQUIRED
+ self.required = kwargs.get('required',
+ askbot_settings.TAGS_ARE_REQUIRED)
self.widget = forms.TextInput(
attrs={'size': 50, 'autocomplete': 'off'}
)
@@ -461,6 +464,17 @@ class SummaryField(forms.CharField):
)
+class EditorForm(forms.Form):
+ """form with one field - `editor`
+ the field must be created dynamically, so it's added
+ in the __init__() function"""
+
+ def __init__(self, editor_attrs=None):
+ super(EditorForm, self).__init__()
+ editor_attrs = editor_attrs or {}
+ self.fields['editor'] = EditorField(editor_attrs=editor_attrs)
+
+
class DumpUploadForm(forms.Form):
"""This form handles importing
data into the forum. At the moment it only
@@ -784,7 +798,7 @@ class PostAsSomeoneForm(forms.Form):
'Can create new accounts.'
),
required=False,
- widget=forms.TextInput(attrs={'class': 'tipped-input'})
+ widget=forms.TextInput()
)
post_author_email = forms.CharField(
initial=_('Email address:'),
@@ -895,6 +909,7 @@ ASK_BY_EMAIL_SUBJECT_HELP = _(
'[tag1, tag2, tag3,...] question title'
)
+#widgetforms
class AskWidgetForm(forms.Form, FormWithHideableFields):
'''Simple form with just the title to ask a question'''
@@ -908,11 +923,65 @@ class AskWidgetForm(forms.Form, FormWithHideableFields):
required=False,
)
- def __init__(self, *args, **kwargs):
+ def __init__(self, include_text=True, *args, **kwargs):
super(AskWidgetForm, self).__init__(*args, **kwargs)
#hide ask_anonymously field
- if askbot_settings.ALLOW_ASK_ANONYMOUSLY is False:
+ if not askbot_settings.ALLOW_ASK_ANONYMOUSLY:
self.hide_field('ask_anonymously')
+ self.fields['text'] = QuestionEditorField()
+ if not include_text:
+ self.hide_field('text')
+ #hack to make it validate
+ self.fields['text'].required = False
+ self.fields['text'].min_length = 0
+
+class CreateAskWidgetForm(forms.Form, FormWithHideableFields):
+ title = forms.CharField(max_length=100)
+ include_text_field = forms.BooleanField(required=False)
+
+ inner_style = forms.CharField(
+ widget=forms.Textarea,
+ required=False
+ )
+ outer_style = forms.CharField(
+ widget=forms.Textarea,
+ required=False
+ )
+
+ def __init__(self, *args, **kwargs):
+ from askbot.models import Tag
+ super(CreateAskWidgetForm, self).__init__(*args, **kwargs)
+ self.fields['group'] = forms.ModelChoiceField(
+ queryset=get_groups().exclude(name__startswith='_internal_'),
+ required=False
+ )
+ self.fields['tag'] = forms.ModelChoiceField(queryset=Tag.objects.get_content_tags(),
+ required=False)
+ if not askbot_settings.GROUPS_ENABLED:
+ self.hide_field('group')
+
+class CreateQuestionWidgetForm(forms.Form, FormWithHideableFields):
+ title = forms.CharField(max_length=100)
+ question_number = forms.CharField(initial='7')
+ tagnames = forms.CharField(label=_('tags'), max_length=50)
+ search_query = forms.CharField(max_length=50, required=False)
+ order_by = forms.ChoiceField(
+ choices=const.SEARCH_ORDER_BY,
+ initial='-added_at'
+ )
+ style = forms.CharField(
+ widget=forms.Textarea,
+ initial=const.DEFAULT_QUESTION_WIDGET_STYLE,
+ required=False
+ )
+
+ def __init__(self, *args, **kwargs):
+ super(CreateQuestionWidgetForm, self).__init__(*args, **kwargs)
+ self.fields['tagnames'] = TagNamesField()
+ self.fields['group'] = forms.ModelChoiceField(
+ queryset=get_groups().exclude(name__startswith='_internal'),
+ required=False
+ )
class AskByEmailForm(forms.Form):
""":class:`~askbot.forms.AskByEmailForm`
diff --git a/askbot/mail/__init__.py b/askbot/mail/__init__.py
index e653a0e3..01ffb6f0 100644
--- a/askbot/mail/__init__.py
+++ b/askbot/mail/__init__.py
@@ -325,7 +325,8 @@ def process_parts(parts, reply_code = None):
def process_emailed_question(
- from_address, subject, body_text, stored_files, tags = None
+ from_address, subject, body_text, stored_files,
+ tags=None, group_id=None
):
"""posts question received by email or bounces the message"""
#a bunch of imports here, to avoid potential circular import issues
@@ -375,11 +376,12 @@ def process_emailed_question(
user.post_question(
- title = title,
- tags = tagnames.strip(),
- body_text = stripped_body_text,
- by_email = True,
- email_address = from_address
+ title=title,
+ tags=tagnames.strip(),
+ body_text=stripped_body_text,
+ by_email=True,
+ email_address=from_address,
+ group_id=group_id
)
else:
raise ValidationError()
diff --git a/askbot/mail/lamson_handlers.py b/askbot/mail/lamson_handlers.py
index 8de3bd71..59d707c7 100644
--- a/askbot/mail/lamson_handlers.py
+++ b/askbot/mail/lamson_handlers.py
@@ -6,7 +6,7 @@ from django.template import Context
from django.utils.translation import ugettext as _
from lamson.routing import route, stateless
from lamson.server import Relay
-from askbot.models import ReplyAddress, Tag
+from askbot.models import ReplyAddress, Group, Tag
from askbot import mail
from askbot.conf import settings as askbot_settings
from askbot.skins.loaders import get_template
@@ -199,15 +199,12 @@ def ASK(message, host = None, addr = None):
if askbot_settings.GROUP_EMAIL_ADDRESSES_ENABLED == False:
return
try:
- group_tag = Tag.group_tags.get(
- deleted = False,
- name__iexact = addr
- )
+ group = Group.objects.get(name__iexact=addr)
mail.process_emailed_question(
from_address, subject, body_text, stored_files,
- tags = [group_tag.name, ]
+ group_id = group.id
)
- except Tag.DoesNotExist:
+ except Group.DoesNotExist:
#do nothing because this handler will match all emails
return
except Tag.MultipleObjectsReturned:
diff --git a/askbot/management/commands/askbot_add_test_content.py b/askbot/management/commands/askbot_add_test_content.py
index ca250339..888f7df0 100644
--- a/askbot/management/commands/askbot_add_test_content.py
+++ b/askbot/management/commands/askbot_add_test_content.py
@@ -2,6 +2,7 @@ from django.core.management.base import NoArgsCommand
from askbot.models import User
from optparse import make_option
from askbot.utils.console import choice_dialog
+from askbot.conf import settings as askbot_settings
NUM_USERS = 40
@@ -34,6 +35,13 @@ ANSWER_TEMPLATE = BAD_STUFF + """Accelerator photo sharing business school drop
COMMENT_TEMPLATE = BAD_STUFF + """Main differentiators business model micro economics
marketplace equity augmented reality human computer"""
+ALERT_SETTINGS_KEYS = (
+ 'DEFAULT_NOTIFICATION_DELIVERY_SCHEDULE_Q_ASK',
+ 'DEFAULT_NOTIFICATION_DELIVERY_SCHEDULE_Q_ANS',
+ 'DEFAULT_NOTIFICATION_DELIVERY_SCHEDULE_Q_ALL',
+ 'DEFAULT_NOTIFICATION_DELIVERY_SCHEDULE_Q_SEL',
+ 'DEFAULT_NOTIFICATION_DELIVERY_SCHEDULE_M_AND_C',
+)
class Command(NoArgsCommand):
option_list = NoArgsCommand.option_list + (
@@ -41,6 +49,20 @@ class Command(NoArgsCommand):
help='Do not prompt the user for input of any kind.'),
)
+ def save_alert_settings(self):
+ settings = {}
+ for key in ALERT_SETTINGS_KEYS:
+ settings[key] = getattr(askbot_settings, key)
+ self.alert_settings = settings
+
+ def stop_alerts(self):
+ for key in ALERT_SETTINGS_KEYS:
+ askbot_settings.update(key, 'n')
+
+ def restore_saved_alert_settings(self):
+ for key in ALERT_SETTINGS_KEYS:
+ askbot_settings.update(key, self.alert_settings[key])
+
def print_if_verbose(self, text):
"Only print if user chooses verbose output"
if self.verbosity > 0:
@@ -207,7 +229,8 @@ class Command(NoArgsCommand):
if answer != "yes":
return
-
+ self.save_alert_settings()
+ self.stop_alerts()# saves time on running the command
# Create Users
users = self.create_users()
@@ -259,4 +282,6 @@ class Command(NoArgsCommand):
)
self.print_if_verbose("User has accepted a best answer")
+ self.restore_saved_alert_settings()
+
self.print_if_verbose("DONE")
diff --git a/askbot/management/commands/clean_session.py b/askbot/management/commands/clean_session.py
index 6ba9352c..2e663b22 100644
--- a/askbot/management/commands/clean_session.py
+++ b/askbot/management/commands/clean_session.py
@@ -1,13 +1,18 @@
+"""deletes expired sessions from the session database table
+works only when sessions are stored in the database
+"""
from django.core.management.base import NoArgsCommand
from django.contrib.sessions.models import Session
from django.db import transaction
from optparse import make_option
-from askbot.utils.console import print_progress
+from askbot.utils.console import ProgressBar
from datetime import datetime
-DELETE_LIMIT = 1000
+ITEMS_PER_TRANSACTION = 1000
class Command(NoArgsCommand):
+ """Django management command class"""
+
option_list = NoArgsCommand.option_list + (
make_option('--quiet',
action='store_true',
@@ -19,32 +24,23 @@ class Command(NoArgsCommand):
@transaction.commit_manually
def handle_noargs(self, **options):
- '''deletes old sessions'''
+ """deletes old sessions"""
quiet = options.get('quiet', False)
- expired_session_count = Session.objects.filter(expire_date__lt=datetime.now()).count()
- expired_session_list= Session.objects.filter(expire_date__lt=datetime.now()).values_list('session_key', flat=True)
- transaction.commit()
-
- if not quiet:
- print "There are %d expired sessions" % expired_session_count
- range_limit = len(expired_session_list) - 1
- higher_limit = lower_limit = 0
+ expired_sessions = Session.objects.filter(
+ expire_date__lt=datetime.now()
+ )
+ count = expired_sessions.count()
+ expired_sessions = expired_sessions.iterator()
+ if quiet is False:
+ message = 'There are %d expired sessions' % count
+ expired_sessions = ProgressBar(expired_sessions, count, message)
+
+ deleted_count = 0
+ for session in expired_sessions:
+ session.delete()
+ deleted_count += 1
+ if deleted_count % ITEMS_PER_TRANSACTION == 0:
+ transaction.commit()
- for i in range(DELETE_LIMIT, range_limit, DELETE_LIMIT):
- lower_limit = i
- higher_limit = lower_limit + DELETE_LIMIT
- sublist = expired_session_list[lower_limit:higher_limit]
- Session.objects.filter(session_key__in = sublist).delete()
- transaction.commit()
- if not quiet:
- print_progress(higher_limit-1, expired_session_count)
-
- if higher_limit < expired_session_list:
- sublist = expired_session_list[higher_limit:expired_session_count]
- Session.objects.filter(session_key__in = sublist).delete()
- print_progress(expired_session_count, expired_session_count)
- transaction.commit()
-
- if not quiet:
- print "sessions cleared"
+ transaction.commit()
diff --git a/askbot/migrations/0001_initial.py b/askbot/migrations/0001_initial.py
index 97febea3..d11c8f2f 100644
--- a/askbot/migrations/0001_initial.py
+++ b/askbot/migrations/0001_initial.py
@@ -4,39 +4,25 @@ import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
+from askbot.migrations_api import safe_add_column
app_dir_name = os.path.basename(os.path.dirname(os.path.dirname(__file__)))
-def safe_add_column(table, column, column_data, keep_default = False):
- """when user calls syncdb with askbot the first time
- the auth_user table will be created together with the patched columns
- so, we need to add these columns here in separate transactions
- and roll back if they fail, if we want we could also record - which columns clash
- """
- try:
- db.start_transaction()
- db.add_column(table, column, column_data, keep_default = keep_default)
- db.commit_transaction()
- return True
- except:
- db.rollback_transaction()
- return False
-
class Migration(SchemaMigration):
def forwards(self, orm):
#1) patch the existing auth_user table
- safe_add_column('auth_user', 'website', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True), keep_default = False)
- safe_add_column('auth_user', 'about', self.gf('django.db.models.fields.TextField')(blank=True), keep_default = False)
+ safe_add_column('auth_user', 'website', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True, null=True), keep_default = False)
+ safe_add_column('auth_user', 'about', self.gf('django.db.models.fields.TextField')(blank=True, null=True), keep_default = False)
safe_add_column('auth_user', 'hide_ignored_questions', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default = False)
safe_add_column('auth_user', 'gold', self.gf('django.db.models.fields.SmallIntegerField')(default=0), keep_default = False)
safe_add_column('auth_user', 'email_isvalid', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True), keep_default = False)
- safe_add_column('auth_user', 'real_name', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True), keep_default = False)
- safe_add_column('auth_user', 'location', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True), keep_default = False)
+ safe_add_column('auth_user', 'real_name', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True, null=True), keep_default = False)
+ safe_add_column('auth_user', 'location', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True, null=True), keep_default = False)
safe_add_column('auth_user', 'email_key', self.gf('django.db.models.fields.CharField')(max_length=32, null=True), keep_default = False)
safe_add_column('auth_user', 'date_of_birth', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default = False)
safe_add_column('auth_user', 'reputation', self.gf('django.db.models.fields.PositiveIntegerField')(default=1), keep_default = False)
- safe_add_column('auth_user', 'gravatar', self.gf('django.db.models.fields.CharField')(max_length=32), keep_default = False)
+ safe_add_column('auth_user', 'gravatar', self.gf('django.db.models.fields.CharField')(max_length=32, null=True), keep_default = False)
safe_add_column('auth_user', 'bronze', self.gf('django.db.models.fields.SmallIntegerField')(default=0), keep_default = False)
safe_add_column('auth_user', 'tag_filter_setting', self.gf('django.db.models.fields.CharField')(default='ignored', max_length=16), keep_default = False)
safe_add_column('auth_user', 'last_seen', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now), keep_default = False)
diff --git a/askbot/migrations/0133_apply_global_group_to_posts_and_users.py b/askbot/migrations/0133_apply_global_group_to_posts_and_users.py
index 733c4c73..8c14b55d 100644
--- a/askbot/migrations/0133_apply_global_group_to_posts_and_users.py
+++ b/askbot/migrations/0133_apply_global_group_to_posts_and_users.py
@@ -82,7 +82,8 @@ class Migration(DataMigration):
thread.groups.add(group)
done_count += 1
- print 'Added global group to %d threads.\n' % done_count
+ if items.count():
+ print 'Added global group to %d threads.\n' % done_count
post_types = ('question', 'answer')
posts = orm['askbot.Post'].objects.filter(post_type__in=post_types)
@@ -93,7 +94,8 @@ class Migration(DataMigration):
post.groups.add(group)
done_count += 1
- print 'Added global group to %d posts.\n' % done_count
+ if posts.count():
+ print 'Added global group to %d posts.\n' % done_count
comments = orm['askbot.Post'].objects.filter(post_type='comment')
message = 'Copying group information from answers ' +\
@@ -105,7 +107,8 @@ class Migration(DataMigration):
comment.groups.add(*parent_post_groups)
done_count += 1
- print 'Added global group to %d comments.\n' % done_count
+ if comments.count():
+ print 'Added global group to %d comments.\n' % done_count
users = orm['auth.User'].objects.all()
message = 'Adding all users to the global group'
@@ -117,7 +120,8 @@ class Migration(DataMigration):
membership.save()
done_count += 1
- print 'Added global group to %d users.' % done_count
+ if users.count():
+ print 'Added global group to %d users.' % done_count
def backwards(self, orm):
diff --git a/askbot/migrations/0135_auto__add_questionwidget__add_askwidget.py b/askbot/migrations/0135_auto__add_questionwidget__add_askwidget.py
new file mode 100644
index 00000000..dafdafb4
--- /dev/null
+++ b/askbot/migrations/0135_auto__add_questionwidget__add_askwidget.py
@@ -0,0 +1,393 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'QuestionWidget'
+ db.create_table('askbot_questionwidget', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ('question_number', self.gf('django.db.models.fields.PositiveIntegerField')(default=7)),
+ ('tagnames', self.gf('django.db.models.fields.CharField')(max_length=50)),
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Tag'], null=True, blank=True)),
+ ('search_query', self.gf('django.db.models.fields.CharField')(max_length=50, null=True, blank=True, default='')),
+ ('order_by', self.gf('django.db.models.fields.CharField')(default='-added_at', max_length=18)),
+ ('style', self.gf('django.db.models.fields.TextField')(default='', blank=True, null=True)),
+ ))
+ db.send_create_signal('askbot', ['QuestionWidget'])
+
+ # Adding model 'AskWidget'
+ db.create_table('askbot_askwidget', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=100)),
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='groups', null=True, to=orm['askbot.Tag'])),
+ ('tag', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Tag'], null=True, blank=True)),
+ ('include_text_field', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('inner_style', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
+ ('outer_style', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
+ ))
+ db.send_create_signal('askbot', ['AskWidget'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'QuestionWidget'
+ db.delete_table('askbot_questionwidget')
+
+ # Deleting model 'AskWidget'
+ db.delete_table('askbot_askwidget')
+
+
+ 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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'groups'", 'null': 'True', 'to': "orm['askbot.Tag']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Tag']"}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'tag'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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/migrations/0136_auto__add_group__add_threadtogroup__add_unique_threadtogroup_thread_ta.py b/askbot/migrations/0136_auto__add_group__add_threadtogroup__add_unique_threadtogroup_thread_ta.py
new file mode 100644
index 00000000..7ea6c02c
--- /dev/null
+++ b/askbot/migrations/0136_auto__add_group__add_threadtogroup__add_unique_threadtogroup_thread_ta.py
@@ -0,0 +1,417 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'Group'
+ db.create_table('askbot_group', (
+ ('group_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.Group'], unique=True, primary_key=True)),
+ ('logo_url', self.gf('django.db.models.fields.URLField')(max_length=200, null=True)),
+ ('moderate_email', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('is_open', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('preapproved_emails', self.gf('django.db.models.fields.TextField')(default='', null=True, blank=True)),
+ ('preapproved_email_domains', self.gf('django.db.models.fields.TextField')(default='', null=True, blank=True)),
+ ))
+ db.send_create_signal('askbot', ['Group'])
+
+ # Adding field 'PostToGroup.group'
+ db.add_column('askbot_post_groups', 'group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Group'], null=True, blank=True), keep_default=False)
+ db.add_column('askbot_thread_groups', 'group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Group'], null=True, blank=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Removing unique constraint on 'ThreadToGroup', fields ['thread', 'tag']
+ db.delete_unique('askbot_thread_groups', ['thread_id', 'tag_id'])
+
+ # Deleting model 'Group'
+ db.delete_table('askbot_group')
+
+ # Deleting model 'ThreadToGroup'
+ db.delete_table('askbot_thread_groups')
+
+ # Deleting field 'PostToGroup.group'
+ db.delete_column('askbot_post_groups', 'group_id')
+
+ # Changing field 'QuestionWidget.search_query'
+ db.alter_column('askbot_questionwidget', 'search_query', self.gf('django.db.models.fields.CharField')(default=None, max_length=50))
+
+ # Adding M2M table for field groups on 'Thread'
+ db.create_table('askbot_thread_groups', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('thread', models.ForeignKey(orm['askbot.thread'], null=False)),
+ ('tag', models.ForeignKey(orm['askbot.tag'], null=False))
+ ))
+ db.create_unique('askbot_thread_groups', ['thread_id', 'tag_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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'groups'", 'null': 'True', 'to': "orm['askbot.Tag']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', '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.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Tag']"}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'tag'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Tag']"}),
+ '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.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'tag'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']"}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"})
+ },
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0137_create_groups_from_relevant_tags.py b/askbot/migrations/0137_create_groups_from_relevant_tags.py
new file mode 100644
index 00000000..8d9a55d2
--- /dev/null
+++ b/askbot/migrations/0137_create_groups_from_relevant_tags.py
@@ -0,0 +1,447 @@
+# encoding: utf-8
+import datetime
+import os
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+import askbot
+from askbot.utils.console import ProgressBar
+from askbot.search.postgresql import setup_full_text_search
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ profiles = orm['askbot.GroupProfile'].objects.all()
+ items = profiles.iterator()
+ count = profiles.count()
+ message = 'Transfering group information from Tag to Group model'
+ for profile in ProgressBar(items, count, message):
+ group_tag = profile.group_tag
+ group_name = group_tag.name.replace('-', ' ')
+ group = orm['askbot.Group']()
+ group.name = group_name
+ group.logo_url = profile.logo_url
+ group.moderate_email = profile.moderate_email
+ group.is_open = profile.is_open
+ group.preapproved_emails = profile.preapproved_emails
+ group.preapproved_email_domains = profile.preapproved_email_domains
+
+ try:
+ #see if such group is already there
+ auth_group = orm['auth.Group'].objects.get(name=group_name)
+ group.group_ptr = auth_group
+ except orm['auth.Group'].DoesNotExist:
+ pass
+
+ group.save()
+
+ #update thread groups
+ thread_groups = orm['askbot.ThreadToGroup'].objects
+ thread_groups = thread_groups.filter(tag=group_tag)
+ thread_groups.update(group=group)
+ #update post groups
+ post_groups = orm['askbot.PostToGroup'].objects
+ post_groups = post_groups.filter(tag=group_tag)
+ post_groups.update(group=group)
+ #update user groups
+ memberships = group_tag.user_memberships.all()
+ for membership in memberships:
+ membership.user.groups.add(group)
+
+ db_engine_name = askbot.get_database_engine_name()
+ if 'postgresql_psycopg2' in db_engine_name:
+ from django.db import connection
+ cursor = connection.cursor()
+ cursor.execute(
+ 'DROP TRIGGER group_membership_tsv_update_trigger '
+ 'ON askbot_groupmembership'
+ )
+
+ message = 'Deleting old group information'
+ items = profiles.iterator()
+ for profile in ProgressBar(items, count, message):
+ group_tag = profile.group_tag
+ group_tag.user_memberships.all().delete()
+ profile.delete()
+
+ #for postgresql setup new user full text search
+ if 'postgresql_psycopg2' in db_engine_name:
+
+ script_path = os.path.join(
+ askbot.get_install_directory(),
+ 'search', 'postgresql',
+ 'user_profile_search_08312012.plsql'
+ )
+ setup_full_text_search(script_path)
+
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+
+
+ models = {
+ 'askbot.activity': {
+ 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
+ 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.anonymousanswer': {
+ 'Meta': {'object_name': 'AnonymousAnswer'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ '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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'groups'", 'null': 'True', 'to': "orm['askbot.Tag']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', '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.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Tag']"}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'tag'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Tag']"}),
+ '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.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'tag'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']"}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"})
+ },
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0138_auto__del_groupprofile__del_groupmembership__del_unique_groupmembershi.py b/askbot/migrations/0138_auto__del_groupprofile__del_groupmembership__del_unique_groupmembershi.py
new file mode 100644
index 00000000..866bd8a0
--- /dev/null
+++ b/askbot/migrations/0138_auto__del_groupprofile__del_groupmembership__del_unique_groupmembershi.py
@@ -0,0 +1,441 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Removing unique constraint on 'ThreadToGroup', fields ['tag', 'thread']
+ db.delete_unique('askbot_thread_groups', ['tag_id', 'thread_id'])
+
+ # Removing unique constraint on 'PostToGroup', fields ['post', 'tag']
+ db.delete_unique('askbot_post_groups', ['post_id', 'tag_id'])
+
+ # Removing unique constraint on 'GroupMembership', fields ['group', 'user']
+ db.delete_unique('askbot_groupmembership', ['group_id', 'user_id'])
+
+ # Deleting model 'GroupProfile'
+ db.delete_table('askbot_groupprofile')
+
+ # Deleting model 'GroupMembership'
+ db.delete_table('askbot_groupmembership')
+
+ # Deleting field 'PostToGroup.tag'
+ db.delete_column('askbot_post_groups', 'tag_id')
+
+ # Changing field 'PostToGroup.group'
+ db.alter_column('askbot_post_groups', 'group_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['askbot.Group']))
+
+ # Adding unique constraint on 'PostToGroup', fields ['post', 'group']
+ db.create_unique('askbot_post_groups', ['post_id', 'group_id'])
+
+ # Deleting field 'ThreadToGroup.tag'
+ db.delete_column('askbot_thread_groups', 'tag_id')
+
+ # Changing field 'ThreadToGroup.group'
+ db.alter_column('askbot_thread_groups', 'group_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['askbot.Group']))
+
+ # Adding unique constraint on 'ThreadToGroup', fields ['group', 'thread']
+ db.create_unique('askbot_thread_groups', ['group_id', 'thread_id'])
+
+
+ def backwards(self, orm):
+
+ # Removing unique constraint on 'ThreadToGroup', fields ['group', 'thread']
+ db.delete_unique('askbot_thread_groups', ['group_id', 'thread_id'])
+
+ # Removing unique constraint on 'PostToGroup', fields ['post', 'group']
+ db.delete_unique('askbot_post_groups', ['post_id', 'group_id'])
+
+ # Adding model 'GroupProfile'
+ db.create_table('askbot_groupprofile', (
+ ('preapproved_emails', self.gf('django.db.models.fields.TextField')(default='', null=True, blank=True)),
+ ('is_open', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('preapproved_email_domains', self.gf('django.db.models.fields.TextField')(default='', null=True, blank=True)),
+ ('moderate_email', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('logo_url', self.gf('django.db.models.fields.URLField')(max_length=200, null=True)),
+ ('group_tag', self.gf('django.db.models.fields.related.OneToOneField')(related_name='group_profile', unique=True, to=orm['askbot.Tag'])),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ))
+ db.send_create_signal('askbot', ['GroupProfile'])
+
+ # Adding model 'GroupMembership'
+ db.create_table('askbot_groupmembership', (
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')(related_name='user_memberships', to=orm['askbot.Tag'])),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='group_memberships', to=orm['auth.User'])),
+ ))
+ db.send_create_signal('askbot', ['GroupMembership'])
+
+ # Adding unique constraint on 'GroupMembership', fields ['group', 'user']
+ db.create_unique('askbot_groupmembership', ['group_id', 'user_id'])
+
+ # Adding field 'PostToGroup.tag'
+ db.add_column('askbot_post_groups', 'tag', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['askbot.Tag']), keep_default=False)
+
+ # Changing field 'PostToGroup.group'
+ db.alter_column('askbot_post_groups', 'group_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Group'], null=True))
+
+ # Adding unique constraint on 'PostToGroup', fields ['post', 'tag']
+ db.create_unique('askbot_post_groups', ['post_id', 'tag_id'])
+
+ # Adding field 'ThreadToGroup.tag'
+ db.add_column('askbot_thread_groups', 'tag', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['askbot.Tag']), keep_default=False)
+
+ # Changing field 'ThreadToGroup.group'
+ db.alter_column('askbot_thread_groups', 'group_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Group'], null=True))
+
+ # Adding unique constraint on 'ThreadToGroup', fields ['tag', 'thread']
+ db.create_unique('askbot_thread_groups', ['tag_id', 'thread_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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'groups'", 'null': 'True', 'to': "orm['askbot.Tag']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', '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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"})
+ },
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0139_add__openness__field_to_group.py b/askbot/migrations/0139_add__openness__field_to_group.py
new file mode 100644
index 00000000..e909782a
--- /dev/null
+++ b/askbot/migrations/0139_add__openness__field_to_group.py
@@ -0,0 +1,359 @@
+# -*- 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 'Group.openness'
+ db.add_column('askbot_group', 'openness',
+ self.gf('django.db.models.fields.SmallIntegerField')(default=2),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting field 'Group.openness'
+ db.delete_column('askbot_group', 'openness')
+
+ 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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', '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'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'db_table': "'askbot_thread_groups'", 'to': "orm['askbot.Group']"}),
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0140_move_group_field__is_open__to_openness.py b/askbot/migrations/0140_move_group_field__is_open__to_openness.py
new file mode 100644
index 00000000..36655aea
--- /dev/null
+++ b/askbot/migrations/0140_move_group_field__is_open__to_openness.py
@@ -0,0 +1,380 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+from askbot.utils.console import ProgressBar
+
+#copied from askbot.models.Group for reference
+OPENNESS_CHOICES = (
+ (0, 'open'),
+ (1, 'moderated'),
+ (2, 'closed'),
+)
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ groups = orm['askbot.Group'].objects.all()
+ objects = groups.iterator()
+ count = groups.count()
+ message = 'Translating group is_open to openness setting'
+ for group in ProgressBar(objects, count, message):
+ if group.is_open:
+ group.openness = 0
+ group.save()
+
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+ groups = orm['askbot.Group'].objects.all()
+ objects = groups.iterator()
+ count = groups.count()
+ message = 'Translating group openness to back to is_open setting'
+ for group in ProgressBar(objects, count, message):
+ group.is_open = group.openness in (0, 1)
+ group.save()
+
+
+ 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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', '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'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'db_table': "'askbot_thread_groups'", 'to': "orm['askbot.Group']"}),
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
+ symmetrical = True
diff --git a/askbot/migrations/0141_auto__del_field_group_is_open.py b/askbot/migrations/0141_auto__del_field_group_is_open.py
new file mode 100644
index 00000000..0c0c1803
--- /dev/null
+++ b/askbot/migrations/0141_auto__del_field_group_is_open.py
@@ -0,0 +1,358 @@
+# -*- 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):
+ # Deleting field 'Group.is_open'
+ db.delete_column('askbot_group', 'is_open')
+
+ def backwards(self, orm):
+ # Adding field 'Group.is_open'
+ db.add_column('askbot_group', 'is_open',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ 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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'db_table': "'askbot_thread_groups'", 'to': "orm['askbot.Group']"}),
+ '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_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot'] \ No newline at end of file
diff --git a/askbot/migrations/0142_auto__add_groupmembership.py b/askbot/migrations/0142_auto__add_groupmembership.py
new file mode 100644
index 00000000..80c5ba22
--- /dev/null
+++ b/askbot/migrations/0142_auto__add_groupmembership.py
@@ -0,0 +1,371 @@
+# -*- 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 'GroupMembership'
+ db.create_table('askbot_groupmembership', (
+ ('authusergroups_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.AuthUserGroups'], unique=True, primary_key=True)),
+ ('level', self.gf('django.db.models.fields.SmallIntegerField')(default=1)),
+ ))
+ db.send_create_signal('askbot', ['GroupMembership'])
+
+ def backwards(self, orm):
+ # Deleting model 'GroupMembership'
+ db.delete_table('askbot_groupmembership')
+
+ 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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'db_table': "'askbot_thread_groups'", 'to': "orm['askbot.Group']"}),
+ '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.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot'] \ No newline at end of file
diff --git a/askbot/migrations/0143_populate_group_memberships.py b/askbot/migrations/0143_populate_group_memberships.py
new file mode 100644
index 00000000..b2fd086d
--- /dev/null
+++ b/askbot/migrations/0143_populate_group_memberships.py
@@ -0,0 +1,378 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+from askbot.utils.console import ProgressBar
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ auth_memberships = orm['auth.AuthUserGroups'].objects.all()
+ count = auth_memberships.count()
+ objects = auth_memberships.iterator()
+ message = 'Populating Askbot group memberships'
+
+ for auth_membership in ProgressBar(objects, count, message):
+ membership = orm['askbot.GroupMembership']()
+ membership.authusergroups_ptr = auth_membership
+ membership.group = auth_membership.group
+ membership.user = auth_membership.user
+ membership.save()
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+
+
+ models = {
+ 'askbot.activity': {
+ 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
+ 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.anonymousanswer': {
+ 'Meta': {'object_name': 'AnonymousAnswer'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Post']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ '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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'db_table': "'askbot_thread_groups'", 'to': "orm['askbot.Group']"}),
+ '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.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
+ symmetrical = True
diff --git a/askbot/migrations/0144_auto__add_field_questionwidget_group__add_field_askwidget_group.py b/askbot/migrations/0144_auto__add_field_questionwidget_group__add_field_askwidget_group.py
new file mode 100644
index 00000000..cb2a63f0
--- /dev/null
+++ b/askbot/migrations/0144_auto__add_field_questionwidget_group__add_field_askwidget_group.py
@@ -0,0 +1,384 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'QuestionWidget.group'
+ try:
+ db.delete_column('askbot_askwidget', 'group_id')
+ db.delete_column('askbot_questionwidget', 'group_id')
+ except:
+ pass
+
+ db.add_column('askbot_questionwidget', 'group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Group'], null=True, blank=True), keep_default=False)
+
+ # Adding field 'AskWidget.group'
+ db.add_column('askbot_askwidget', 'group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Group'], null=True, blank=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'QuestionWidget.group'
+ db.delete_column('askbot_questionwidget', 'group_id')
+
+ # Deleting field 'AskWidget.group'
+ db.delete_column('askbot_askwidget', 'group_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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'db_table': "'askbot_thread_groups'", 'to': "orm['askbot.Group']"}),
+ '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.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0145_add_moderate_answers_to_enquirers__to_askbot_group.py b/askbot/migrations/0145_add_moderate_answers_to_enquirers__to_askbot_group.py
new file mode 100644
index 00000000..74735130
--- /dev/null
+++ b/askbot/migrations/0145_add_moderate_answers_to_enquirers__to_askbot_group.py
@@ -0,0 +1,378 @@
+# -*- 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 'Group.moderate_answers_to_enquirers'
+ db.add_column('askbot_group', 'moderate_answers_to_enquirers',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting field 'Group.moderate_answers_to_enquirers'
+ db.delete_column('askbot_group', 'moderate_answers_to_enquirers')
+
+ 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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0146_auto__add_field_threadtogroup_visibility.py b/askbot/migrations/0146_auto__add_field_threadtogroup_visibility.py
new file mode 100644
index 00000000..bc1d4377
--- /dev/null
+++ b/askbot/migrations/0146_auto__add_field_threadtogroup_visibility.py
@@ -0,0 +1,379 @@
+# -*- 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 'ThreadToGroup.visibility'
+ db.add_column('askbot_thread_groups', 'visibility',
+ self.gf('django.db.models.fields.SmallIntegerField')(default=1),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting field 'ThreadToGroup.visibility'
+ db.delete_column('askbot_thread_groups', 'visibility')
+
+ 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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot'] \ No newline at end of file
diff --git a/askbot/migrations/0147_auto__add_field_group_description.py b/askbot/migrations/0147_auto__add_field_group_description.py
new file mode 100644
index 00000000..223fd974
--- /dev/null
+++ b/askbot/migrations/0147_auto__add_field_group_description.py
@@ -0,0 +1,380 @@
+# -*- 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 'Group.description'
+ db.add_column('askbot_group', 'description',
+ self.gf('django.db.models.fields.related.OneToOneField')(blank=True, related_name='described_group', unique=True, null=True, to=orm['askbot.Post']),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting field 'Group.description'
+ db.delete_column('askbot_group', 'description_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.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.BadgeData']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", 'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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', [], {}),
+ '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.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.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']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot'] \ No newline at end of file
diff --git a/askbot/migrations_api/__init__.py b/askbot/migrations_api/__init__.py
index 1e213613..586c02d9 100644
--- a/askbot/migrations_api/__init__.py
+++ b/askbot/migrations_api/__init__.py
@@ -4,6 +4,23 @@ by mitigating absence of access to the django model api
since models change with time, this api is implemented in different
versions. Different versions do not need to have all the same functions.
"""
+from south.db import db
+
+def safe_add_column(table, column, column_data, keep_default = False):
+ """when user calls syncdb with askbot the first time
+ the auth_user table will be created together with the patched columns
+ so, we need to add these columns here in separate transactions
+ and roll back if they fail, if we want we could also record - which columns clash
+ """
+ try:
+ db.start_transaction()
+ db.add_column(table, column, column_data, keep_default = keep_default)
+ db.commit_transaction()
+ return True
+ except:
+ db.rollback_transaction()
+ return False
+
class BaseAPI(object):
def __init__(self, orm):
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 0b2f877d..418a5dba 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -1,6 +1,14 @@
from askbot import startup_procedures
startup_procedures.run()
+from django.contrib.auth.models import User
+#set up a possibility for the users to follow others
+try:
+ import followit
+ followit.register(User)
+except ImportError:
+ pass
+
import collections
import datetime
import hashlib
@@ -12,7 +20,6 @@ from django.db.models import signals as django_signals
from django.template import Context
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
-from django.contrib.auth.models import User
from django.utils.safestring import mark_safe
from django.utils.html import escape
from django.db import models
@@ -37,7 +44,8 @@ from askbot.models.tag import get_group_names
from askbot.models.tag import get_groups
from askbot.models.tag import format_personal_group_name
from askbot.models.user import EmailFeedSetting, ActivityAuditStatus, Activity
-from askbot.models.user import GroupMembership, GroupProfile
+from askbot.models.user import GroupMembership
+from askbot.models.user import Group
from askbot.models.post import Post, PostRevision
from askbot.models.post import PostFlagReason, AnonymousAnswer
from askbot.models.post import PostToGroup
@@ -46,6 +54,7 @@ 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, Vote
+from askbot.models.widgets import AskWidget, QuestionWidget
from askbot import auth
from askbot.utils.decorators import auto_now_timestamp
from askbot.utils.slug import slugify
@@ -58,13 +67,6 @@ 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')
- )
-
def get_admin():
"""returns admin with the lowest user ID
if there are no users at all - creates one
@@ -307,7 +309,7 @@ def user_get_marked_tag_names(self, reason):
attr_name = MARKED_TAG_PROPERTY_MAP[reason]
wildcard_tags = getattr(self, attr_name).split()
tag_names.extend(wildcard_tags)
-
+
return tag_names
def user_has_affinity_to_question(self, question = None, affinity_type = None):
@@ -378,7 +380,7 @@ def user_can_have_strong_url(self):
return (self.reputation >= askbot_settings.MIN_REP_TO_HAVE_STRONG_URL)
def user_can_post_by_email(self):
- """True, if reply by email is enabled
+ """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
@@ -401,6 +403,34 @@ def user_get_or_create_fake_user(self, username, email):
user.save()
return user
+def user_notify_users(
+ self, notification_type=None, recipients=None, content_object=None
+):
+ """A utility function that creates instance
+ of :class:`Activity` and adds recipients
+ * `notification_type` - value should be one of TYPE_ACTIVITY_...
+ * `recipients` - an iterable of user objects
+ * `content_object` - any object related to the notification
+
+ todo: possibly add checks on the content_object, depending on the
+ notification_type
+ """
+ activity = Activity(
+ user=self,
+ activity_type=notification_type,
+ content_object=content_object
+ )
+ activity.save()
+ activity.add_recipients(recipients)
+
+def user_get_notifications(self, notification_types=None, **kwargs):
+ """returns query set of activity audit status objects"""
+ return ActivityAuditStatus.objects.filter(
+ user=self,
+ activity__activity_type__in=notification_types,
+ **kwargs
+ )
+
def _assert_user_can(
user = None,
post = None, #related post (may be parent)
@@ -1180,21 +1210,21 @@ def user_post_comment(
)
return comment
-def user_post_tag_wiki(
+def user_post_object_description(
self,
- tag = None,
- body_text = None,
- timestamp = None
+ obj=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
+ """Creates an object description post and assigns it
+ to the given object. Returns the newly created post"""
+ description_post = Post.objects.create_new_tag_wiki(
+ author=self,
+ text=body_text
)
- tag.tag_wiki = tag_wiki_post
- tag.save()
- return tag_wiki_post
+ obj.description = description_post
+ obj.save()
+ return description_post
def user_post_anonymous_askbot_content(user, session_key):
@@ -1823,6 +1853,7 @@ def user_post_answer(
is_private = is_private,
by_email = by_email
)
+ #add to the answerer's group
answer_post.add_to_groups([self.get_personal_group()])
answer_post.thread.invalidate_cached_data()
@@ -2199,11 +2230,11 @@ def get_profile_link(self):
def user_get_groups(self, private=False):
"""returns a query set of groups to which user belongs"""
#todo: maybe cache this query
- return Tag.group_tags.get_for_user(self, private=private)
+ return Group.objects.get_for_user(self, private=private)
def user_get_personal_group(self):
group_name = format_personal_group_name(self)
- return Tag.group_tags.get(name=group_name)
+ return Group.objects.get(name=group_name)
def user_get_foreign_groups(self):
"""returns a query set of groups to which user does not belong"""
@@ -2227,16 +2258,24 @@ def user_can_make_group_private_posts(self):
"""simplest implementation: user belongs to at least one group"""
return self.get_groups(private=True).count() > 0
+def user_get_group_membership(self, group):
+ """returns a group membership object or None
+ if it is not there
+ """
+ try:
+ return GroupMembership.objects.get(user=self, group=group)
+ except GroupMembership.DoesNotExist:
+ return None
+
+
def user_get_groups_membership_info(self, groups):
- """returts a defaultdict with values that are
+ """returns a defaultdict with values that are
dictionaries with the following keys and values:
- * key: can_join, value: True if user can join group
- * key: is_member, value: True if user is member of group
+ * key: acceptance_level, value: 'closed', 'moderated', 'open'
+ * key: membership_level, value: 'none', 'pending', 'full'
``groups`` is a group tag query set
"""
- groups = groups.select_related('group_profile')
-
group_ids = groups.values_list('id', flat = True)
memberships = GroupMembership.objects.filter(
user__id = self.id,
@@ -2244,17 +2283,16 @@ def user_get_groups_membership_info(self, groups):
)
info = collections.defaultdict(
- lambda: {'can_join': False, 'is_member': False}
+ lambda: {'acceptance_level': 'closed', 'membership_level': 'none'}
)
for membership in memberships:
- info[membership.group_id]['is_member'] = True
+ membership_level = membership.get_level_display()
+ info[membership.group_id]['membership_level'] = membership_level
for group in groups:
- info[group.id]['can_join'] = group.group_profile.can_accept_user(self)
+ info[group.id]['acceptance_level'] = group.get_openness_level_for_user(self)
return info
-
-
def user_get_karma_summary(self):
"""returns human readable sentence about
@@ -2398,7 +2436,7 @@ def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None):
auth.onDownVotedCanceled(vote, post, user, timestamp)
else:
auth.onDownVoted(vote, post, user, timestamp)
-
+
post.thread.invalidate_cached_data()
if post.post_type == 'question':
@@ -2479,27 +2517,30 @@ def user_approve_post_revision(user, post_revision, timestamp = None):
)
@auto_now_timestamp
-def flag_post(user, post, timestamp=None, cancel=False, cancel_all = False, force = False):
+def flag_post(
+ user, post, timestamp=None, cancel=False, cancel_all=False, force=False
+):
if cancel_all:
# remove all flags
if force == False:
- user.assert_can_remove_all_flags_offensive(post = post)
+ user.assert_can_remove_all_flags_offensive(post=post)
post_content_type = ContentType.objects.get_for_model(post)
all_flags = Activity.objects.filter(
- activity_type = const.TYPE_ACTIVITY_MARK_OFFENSIVE,
- content_type = post_content_type, object_id=post.id
+ activity_type=const.TYPE_ACTIVITY_MARK_OFFENSIVE,
+ content_type=post_content_type,
+ object_id=post.id
)
for flag in all_flags:
- auth.onUnFlaggedItem(post, flag.user, timestamp=timestamp)
+ auth.onUnFlaggedItem(post, flag.user, timestamp=timestamp)
elif cancel:#todo: can't unflag?
if force == False:
user.assert_can_remove_flag_offensive(post = post)
- auth.onUnFlaggedItem(post, user, timestamp=timestamp)
+ auth.onUnFlaggedItem(post, user, timestamp=timestamp)
else:
if force == False:
- user.assert_can_flag_offensive(post = post)
+ user.assert_can_flag_offensive(post=post)
auth.onFlaggedItem(post, user, timestamp=timestamp)
award_badges_signal.send(None,
event = 'flag_post',
@@ -2605,29 +2646,54 @@ def user_update_wildcard_tag_selections(
return new_tags
-def user_edit_group_membership(self, user = None, group = None, action = None):
+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
+
+ returns instance of GroupMembership (if action is "add") or None
"""
if action == 'add':
- GroupMembership.objects.get_or_create(user = user, group = group)
+ #calculate new level
+ openness = group.get_openness_level_for_user(user)
+
+ #let people join these special groups, but not leave
+ if group.name == askbot_settings.GLOBAL_GROUP_NAME:
+ openness = 'open'
+ elif group.name == format_personal_group_name(user):
+ openness = 'open'
+
+ if openness == 'open':
+ level = GroupMembership.FULL
+ elif openness == 'moderated':
+ level = GroupMembership.PENDING
+ elif openness == 'closed':
+ raise django_exceptions.PermissionDenied()
+
+ membership, created = GroupMembership.objects.get_or_create(
+ user=user, group=group, level=level
+ )
+ return membership
+
elif action == 'remove':
GroupMembership.objects.get(user = user, group = group).delete()
+ return None
else:
raise ValueError('invalid action')
def user_join_group(self, group):
- self.edit_group_membership(group=group, user=self, action='add')
+ return self.edit_group_membership(group=group, user=self, action='add')
def user_leave_group(self, group):
self.edit_group_membership(group=group, user=self, action='remove')
-def user_is_group_member(self, group = None):
- return self.group_memberships.filter(group = group).count() == 1
+def user_is_group_member(self, group=None):
+ return GroupMembership.objects.filter(
+ user=self, group=group
+ ).count() == 1
User.add_to_class(
'add_missing_askbot_subscriptions',
@@ -2654,8 +2720,10 @@ User.add_to_class('get_marked_tags', user_get_marked_tags)
User.add_to_class('get_marked_tag_names', user_get_marked_tag_names)
User.add_to_class('get_groups', user_get_groups)
User.add_to_class('get_foreign_groups', user_get_foreign_groups)
+User.add_to_class('get_group_membership', user_get_group_membership)
User.add_to_class('get_personal_group', user_get_personal_group)
User.add_to_class('get_primary_group', user_get_primary_group)
+User.add_to_class('get_notifications', user_get_notifications)
User.add_to_class('strip_email_signature', user_strip_email_signature)
User.add_to_class('get_groups_membership_info', user_get_groups_membership_info)
User.add_to_class('get_anonymous_name', user_get_anonymous_name)
@@ -2675,7 +2743,7 @@ 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('post_object_description', user_post_object_description)
User.add_to_class('visit_question', user_visit_question)
User.add_to_class('upvote', upvote)
User.add_to_class('downvote', downvote)
@@ -2739,6 +2807,7 @@ User.add_to_class(
user_update_wildcard_tag_selections
)
User.add_to_class('approve_post_revision', user_approve_post_revision)
+User.add_to_class('notify_users', user_notify_users)
#assertions
User.add_to_class('assert_can_vote_for_post', user_assert_can_vote_for_post)
@@ -2854,17 +2923,17 @@ def format_instant_notification_email(
if update_type.endswith('update'):
user_action = _('%(user)s edited a %(post_link)s.')
else:
- user_action = _('%(user)s posted a %(post_link)s')
+ 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.')
+ 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.')
+ user_action = _('%(user)s posted a %(post_link)s.')
else:
raise ValueError('unrecognized post type')
@@ -2892,7 +2961,7 @@ def format_instant_notification_email(
reply_separator += '</p>'
else:
reply_separator = user_action
-
+
update_data = {
'update_author_name': from_user.username,
'receiving_user_name': to_user.username,
@@ -2921,7 +2990,7 @@ def get_reply_to_addresses(user, post):
the first address - always a real email address,
the second address is not ``None`` only for "question" posts.
- When the user is notified of a new question -
+ When the user is notified of a new question -
i.e. `post` is a "quesiton", he/she
will need to choose - whether to give a question or a comment,
thus we return the second address - for the comment reply.
@@ -3016,7 +3085,7 @@ def send_instant_notifications_about_activity_in_post(
update_type = update_type,
template = get_template('instant_notification.html')
)
-
+
headers['Reply-To'] = reply_address
try:
mail.send_mail(
@@ -3046,7 +3115,7 @@ def notify_author_of_published_revision(
if revision.should_notify_author_about_publishing(was_approved):
from askbot.tasks import notify_author_of_published_revision_celery_task
notify_author_of_published_revision_celery_task.delay(revision)
-
+
#todo: move to utils
def calculate_gravatar_hash(instance, **kwargs):
@@ -3258,7 +3327,7 @@ def record_flag_offensive(instance, mark_by, **kwargs):
# recipients = instance.get_author_list(
# exclude_list = [mark_by]
# )
- activity.add_recipients(get_admins_and_moderators())
+ activity.add_recipients(instance.get_moderators())
def remove_flag_offensive(instance, mark_by, **kwargs):
"Remove flagging activity"
@@ -3381,8 +3450,8 @@ def add_user_to_personal_group(sender, instance, created, **kwargs):
#in theore here we may have two users that will have
#identical group names!!!
group_name = format_personal_group_name(instance)
- group = Tag.group_tags.get_or_create(
- group_name=group_name, user=instance
+ group = Group.objects.get_or_create(
+ name=group_name, user=instance
)
instance.edit_group_membership(
group=group, user=instance, action='add'
@@ -3461,7 +3530,7 @@ def update_user_avatar_type_flag(instance, **kwargs):
def make_admin_if_first_user(instance, **kwargs):
"""first user automatically becomes an administrator
the function is run only once in the interpreter session
- """
+ """
import sys
#have to check sys.argv to satisfy the test runner
#which fails with the cache-based skipping
@@ -3475,6 +3544,16 @@ def make_admin_if_first_user(instance, **kwargs):
instance.set_admin_status()
cache.cache.set('admin-created', True)
+def moderate_group_joining(sender, instance=None, created=False, **kwargs):
+ if created and instance.level == GroupMembership.PENDING:
+ user = instance.user
+ group = instance.group
+ user.notify_users(
+ notification_type=const.TYPE_ACTIVITY_ASK_TO_JOIN_GROUP,
+ recipients = group.get_moderators(),
+ content_object = group
+ )
+
#signal for User model save changes
django_signals.pre_save.connect(make_admin_if_first_user, sender=User)
@@ -3487,6 +3566,7 @@ django_signals.post_save.connect(notify_award_message, sender=Award)
django_signals.post_save.connect(record_answer_accepted, sender=Post)
django_signals.post_save.connect(record_vote, sender=Vote)
django_signals.post_save.connect(record_favorite_question, sender=FavoriteQuestion)
+django_signals.post_save.connect(moderate_group_joining, sender=GroupMembership)
if 'avatar' in django_settings.INSTALLED_APPS:
from avatar.models import Avatar
@@ -3511,13 +3591,6 @@ signals.post_updated.connect(record_post_update_activity)
signals.post_revision_published.connect(notify_author_of_published_revision)
signals.site_visited.connect(record_user_visit)
-#set up a possibility for the users to follow others
-try:
- import followit
- followit.register(User)
-except ImportError:
- pass
-
__all__ = [
'signals',
@@ -3548,14 +3621,13 @@ __all__ = [
'ActivityAuditStatus',
'EmailFeedSetting',
'GroupMembership',
- 'GroupProfile',
+ 'Group',
'User',
'ReplyAddress',
'get_model',
- 'get_admins_and_moderators',
'get_group_names',
'get_groups'
]
diff --git a/askbot/models/post.py b/askbot/models/post.py
index 4ae2286e..49220a63 100644
--- a/askbot/models/post.py
+++ b/askbot/models/post.py
@@ -26,6 +26,7 @@ from askbot.utils.slug import slugify
from askbot import const
from askbot.models.user import Activity
from askbot.models.user import EmailFeedSetting
+from askbot.models.user import Group
from askbot.models.user import GroupMembership
from askbot.models.tag import Tag, MarkedTag
from askbot.models.tag import get_groups, tags_match_some_wildcard
@@ -40,18 +41,14 @@ from askbot.models.base import BaseQuerySetManager, DraftContent
from askbot.utils.diff import textDiff as htmldiff
from askbot.utils import mysql
-
class PostToGroup(models.Model):
- """the "trough" table for the
- relation of groups to posts
- """
post = models.ForeignKey('Post')
- tag = models.ForeignKey('Tag')
+ group = models.ForeignKey(Group)
class Meta:
- unique_together = ('post', 'tag')
- db_table = 'askbot_post_groups'
+ unique_together = ('post', 'group')
app_label = 'askbot'
+ db_table = 'askbot_post_groups'
class PostQuerySet(models.query.QuerySet):
@@ -223,7 +220,12 @@ class PostManager(BaseQuerySetManager):
post.last_edited_at = added_at
post.wikified_at = added_at
- post.parse_and_save(author=author, is_private = is_private)
+ #possibly modify the is_private, if one of the groups
+ #mandates explicit publishing of the posts
+ is_private = is_private or \
+ (thread and thread.requires_response_moderation(author))
+
+ post.parse_and_save(author=author, is_private=is_private)
post.add_revision(
author = author,
@@ -232,7 +234,7 @@ class PostManager(BaseQuerySetManager):
comment = const.POST_STATUS['default_version'],
by_email = by_email
)
-
+
return post
#todo: instead of this, have Thread.add_answer()
@@ -323,7 +325,7 @@ class Post(models.Model):
parent = models.ForeignKey('Post', blank=True, null=True, related_name='comments') # Answer or Question for Comment
thread = models.ForeignKey('Thread', blank=True, null=True, default = None, related_name='posts')
- groups = models.ManyToManyField('Tag', through='PostToGroup', related_name = 'group_posts')#used for group-private posts
+ groups = models.ManyToManyField(Group, through='PostToGroup', related_name = 'group_posts')#used for group-private posts
author = models.ForeignKey(User, related_name='posts')
added_at = models.DateTimeField(default=datetime.datetime.now)
@@ -472,7 +474,7 @@ class Post(models.Model):
return data
#todo: when models are merged, it would be great to remove author parameter
- def parse_and_save(self, author = None, **kwargs):
+ def parse_and_save(self, author=None, **kwargs):
"""generic method to use with posts to be used prior to saving
post edit or addition
"""
@@ -510,6 +512,10 @@ class Post(models.Model):
self.add_to_groups(groups)
elif is_private or group_id:
self.make_private(author, group_id = group_id)
+ elif self.thread_id:#is connected to thread
+ #inherit privacy scope from thread
+ thread_groups = self.thread.groups.all()
+ self.add_to_groups(thread_groups)
else:
self.make_public()
@@ -558,24 +564,35 @@ class Post(models.Model):
def is_reject_reason(self):
return self.post_type == 'reject_reason'
+ def get_moderators(self):
+ """returns query set of users who are site administrators
+ and moderators"""
+ user_filter = models.Q(is_superuser=True) | models.Q(status='m')
+ if askbot_settings.GROUPS_ENABLED:
+ user_filter = user_filter & models.Q(groups__in=self.groups.all())
+ return User.objects.filter(user_filter)
+
+ def has_group(self, group):
+ """true if post belongs to the group"""
+ return self.groups.filter(id=group.id).exists()
+
def add_to_groups(self, groups):
#todo: use bulk-creation
for group in groups:
- PostToGroup.objects.get_or_create(post=self, tag=group)
+ PostToGroup.objects.get_or_create(post=self, group=group)
if self.is_answer() or self.is_question():
comments = self.comments.all()
for group in groups:
for comment in comments:
- PostToGroup.objects.get_or_create(post=comment, tag=group)
-
+ PostToGroup.objects.get_or_create(post=comment, group=group)
def remove_from_groups(self, groups):
- PostToGroup.objects.filter(post=self, tag__in=groups).delete()
+ PostToGroup.objects.filter(post=self, group__in=groups).delete()
if self.is_answer() or self.is_question():
comment_ids = self.comments.all().values_list('id', flat=True)
PostToGroup.objects.filter(
post__id__in=comment_ids,
- tag__in=groups
+ group__in=groups
).delete()
@@ -597,15 +614,10 @@ class Post(models.Model):
* sends email alerts to all subscribers to the post
"""
assert(activity_type is not None)
- if self.is_comment():
- #it's just a comment!
- summary = self.text
+ if diff:
+ summary = diff
else:
- #summary = post.get_latest_revision().summary
- if diff:
- summary = diff
- else:
- summary = self.text
+ summary = self.get_snippet()
update_activity = Activity(
user = updated_by,
@@ -647,13 +659,13 @@ class Post(models.Model):
post=self,
recipients=notify_sets['for_email'],
)
-
- def make_private(self, user, group_id = None):
+
+ def make_private(self, user, group_id=None):
"""makes post private within user's groups
todo: this is a copy-paste in thread and post
"""
if group_id:
- group = Tag.group_tags.get(id=group_id)
+ group = Group.objects.get(id=group_id)
groups = [group]
self.add_to_groups(groups)
@@ -661,7 +673,15 @@ class Post(models.Model):
if group != global_group:
self.remove_from_groups((global_group,))
else:
- groups = user.get_groups(private=True)
+ if self.thread_id and self.is_question() is False:
+ #for thread-related responses we base
+ #privacy scope on thread + add a personal group
+ personal_group = user.get_personal_group()
+ thread_groups = self.thread.get_groups_shared_with()
+ groups = set([personal_group]) | set(thread_groups)
+ else:
+ groups = user.get_groups(private=True)
+
self.add_to_groups(groups)
self.remove_from_groups((get_global_group(),))
@@ -1237,7 +1257,7 @@ class Post(models.Model):
result['for_mentions'] = set(mentioned_users) - set(exclude_list)
#what users are included depends on the post type
#for example for question - all Q&A contributors
- #are included, for comments only authors of comments and parent
+ #are included, for comments only authors of comments and parent
#post are included
result['for_inbox'] = self.get_response_receivers(exclude_list=exclude_list)
@@ -1934,7 +1954,7 @@ class PostRevision(models.Model):
text = models.TextField()
approved = models.BooleanField(default=False, db_index=True)
- approved_by = models.ForeignKey(User, null = True, blank = 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
@@ -1968,13 +1988,14 @@ class PostRevision(models.Model):
if self.by_email and self.email_address:
group_name = self.email_address.split('@')[0]
try:
- group = Tag.objects.get(name = group_name, deleted = False)
+ group = Group.objects.get(name = group_name, deleted = False)
return group.group.profile.moderate_email
- except Tag.DoesNotExist:
+ except Group.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
@@ -2015,7 +2036,7 @@ class PostRevision(models.Model):
body_text = body_text,
recipient_list = [self.author.email,],
)
-
+
else:
message = _(
'Your post was placed on the moderation queue '
@@ -2031,7 +2052,7 @@ class PostRevision(models.Model):
#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
+ from askbot.models import Activity
activity = Activity(
user = self.author,
content_object = self,
@@ -2040,7 +2061,7 @@ class PostRevision(models.Model):
)
activity.save()
#todo: make this group-sensitive
- activity.add_recipients(get_admins_and_moderators())
+ activity.add_recipients(self.post.get_moderators())
def moderate_or_publish(self):
"""either place on moderation queue or announce
diff --git a/askbot/models/question.py b/askbot/models/question.py
index d5f56d21..98a38c90 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -25,6 +25,7 @@ from askbot.models.base import DraftContent, BaseQuerySetManager
from askbot.models.tag import Tag, get_groups
from askbot.models.post import Post, PostRevision
from askbot.models.post import PostToGroup
+from askbot.models.user import Group
from askbot.models import signals
from askbot import const
from askbot.utils.lists import LazyList
@@ -145,7 +146,7 @@ class ThreadManager(BaseQuerySetManager):
)
author_group = author.get_personal_group()
- thread.add_to_groups([author_group])
+ thread.add_to_groups([author_group], visibility=ThreadToGroup.SHOW_PUBLISHED_RESPONSES)
question.add_to_groups([author_group])
if is_private or group_id:#add groups to thread and question
@@ -435,6 +436,30 @@ class ThreadManager(BaseQuerySetManager):
return self.filter(id__in = thread_ids)
+class ThreadToGroup(models.Model):
+ """the "through" many-to-many relation between
+ threads and groups - to distinguish full and "what's published"
+ visibility of threads to various groups
+ """
+ SHOW_PUBLISHED_RESPONSES = 0
+ SHOW_ALL_RESPONSES = 1
+ VISIBILITY_CHOICES = (
+ (SHOW_PUBLISHED_RESPONSES, 'show only published responses'),
+ (SHOW_ALL_RESPONSES, 'show all responses')
+ )
+ thread = models.ForeignKey('Thread')
+ group = models.ForeignKey(Group)
+ visibility = models.SmallIntegerField(
+ choices=VISIBILITY_CHOICES,
+ default=SHOW_ALL_RESPONSES
+ )
+
+ class Meta:
+ unique_together = ('thread', 'group')
+ db_table = 'askbot_thread_groups'
+ app_label = 'askbot'
+
+
class Thread(models.Model):
SUMMARY_CACHE_KEY_TPL = 'thread-question-summary-%d'
ANSWER_LIST_KEY_TPL = 'thread-answer-list-%d'
@@ -442,7 +467,7 @@ class Thread(models.Model):
title = models.CharField(max_length=300)
tags = models.ManyToManyField('Tag', related_name='threads')
- groups = models.ManyToManyField('Tag', related_name='group_threads')
+ groups = models.ManyToManyField(Group, through=ThreadToGroup, related_name='group_threads')
# Denormalised data, transplanted from Question
tagnames = models.CharField(max_length=125)
@@ -503,6 +528,76 @@ class Thread(models.Model):
else:
return self.get_answers(user).count()
+ def get_sharing_info(self, visitor=None):
+ """returns a dictionary with abbreviated thread sharing info:
+ * users - up to a certain number of users, excluding the visitor
+ * groups - up to a certain number of groups
+ * more_users_count - remaining count of shared-with users
+ * more_groups_count - remaining count of shared-with groups
+ """
+ shared_users = self.get_users_shared_with(
+ max_count=2,#"visitor" is implicit
+ exclude_user=visitor
+ )
+ groups = self.groups
+ ugroups = groups.filter(name__startswith='_internal_')
+ ggroups = groups.exclude(name__startswith='_internal_')
+
+ sharing_info = {
+ 'users': shared_users,
+ 'groups': self.get_groups_shared_with(max_count=3),
+ 'more_users_count': max(0, ugroups.count() - 3),
+ 'more_groups_count': max(0, ggroups.count() - 3)
+ }
+ return sharing_info
+
+ def get_users_shared_with(self, max_count=None, exclude_user=None):
+ """returns query set of users with whom
+ this thread is shared
+ """
+ filter = models.Q(
+ thread=self,
+ visibility=ThreadToGroup.SHOW_ALL_RESPONSES
+ ) & models.Q(
+ group__name__startswith='_internal_'
+ )
+
+ if exclude_user:
+ user_group = exclude_user.get_personal_group()
+ filter = filter & ~models.Q(group_id=user_group.id)
+
+ thread_groups = ThreadToGroup.objects.filter(filter)
+
+ if max_count:
+ thread_groups = thread_groups[:max_count]
+
+ group_ids = thread_groups.values_list('group_id', flat=True)
+
+ from askbot.models import GroupMembership
+ user_ids = GroupMembership.objects.filter(
+ group__id__in=group_ids
+ ).values_list(
+ 'user__id', flat=True
+ )
+
+ return User.objects.filter(id__in=user_ids)
+
+ def get_groups_shared_with(self, max_count=None):
+ """returns query set of groups with whom thread is shared"""
+ thread_groups = ThreadToGroup.objects.filter(
+ models.Q(
+ thread=self,
+ visibility=ThreadToGroup.SHOW_ALL_RESPONSES
+ ) & ~models.Q(
+ group__name__startswith='_internal_'
+ )
+ )
+ if max_count:
+ thread_groups = thread_groups[:max_count]
+
+ group_ids = thread_groups.values_list('group_id', flat=True)
+ return Group.objects.filter(id__in=group_ids)
+
def update_favorite_count(self):
self.favourite_count = FavoriteQuestion.objects.filter(thread=self).count()
self.save()
@@ -568,7 +663,7 @@ class Thread(models.Model):
def format_for_email(self, user=None):
"""experimental function: output entire thread for email"""
- question, answers, junk = self.get_cached_post_data(user=user)
+ question, answers, junk, published_ans_ids = self.get_cached_post_data(user=user)
output = question.format_for_email_as_subthread()
if answers:
answer_heading = ungettext(
@@ -589,6 +684,28 @@ class Thread(models.Model):
#use len to cache the queryset
return len(self.get_answers_by_user(user)) > 0
+ def has_moderator(self, user):
+ """true if ``user`` is also a thread moderator"""
+ if user.is_anonymous():
+ return False
+ elif askbot_settings.GROUPS_ENABLED:
+ if user.is_administrator_or_moderator():
+ user_groups = user.get_groups(private=True)
+ thread_groups = self.get_groups_shared_with()
+ return bool(set(user_groups) & set(thread_groups))
+ return False
+
+ def requires_response_moderation(self, author):
+ """true, if answers by a given author must be moderated
+ before publishing to the enquirers"""
+ author_groups = author.get_groups()
+ thread_groups = self.get_groups_shared_with()
+ for group in set(author_groups) & set(thread_groups):
+ if group.moderate_answers_to_enquirers:
+ return True
+
+ return False
+
def tagname_meta_generator(self):
return u','.join([unicode(tag) for tag in self.get_tag_names()])
@@ -635,7 +752,7 @@ class Thread(models.Model):
the method get_post_data()"""
if askbot_settings.GROUPS_ENABLED:
#temporary plug: bypass cache where groups are enabled
- return self.get_post_data(sort_method = sort_method, user = user)
+ return self.get_post_data(sort_method=sort_method, user=user)
key = self.get_post_data_cache_key(sort_method)
post_data = cache.cache.get(key)
if not post_data:
@@ -643,9 +760,10 @@ class Thread(models.Model):
cache.cache.set(key, post_data, const.LONG_TIME)
return post_data
- def get_post_data(self, sort_method = 'votes', user = None):
+ def get_post_data(self, sort_method='votes', user=None):
"""returns question, answers as list and a list of post ids
- for the given thread
+ for the given thread, and the list of published post ids
+ (four values)
the returned posts are pre-stuffed with the comments
all (both posts and the comments sorted in the correct
order)
@@ -713,7 +831,31 @@ class Thread(models.Model):
answers.remove(accepted_answer)
answers.insert(0, accepted_answer)
- return (question_post, answers, post_to_author)
+ #if user is not an inquirer, and thread is moderated,
+ #put published answers first
+ #todo: there may be > 1 enquirers
+ published_answer_ids = list()
+ if self.is_moderated() and user != question_post.author:
+ #if moderated - then author is guaranteed to be the
+ #limited visibility enquirer
+ published_answers = self.posts.get_answers(
+ user=question_post.author#todo: may be > 1
+ ).filter(
+ deleted=False
+ ).order_by(
+ {
+ 'latest':'-added_at',
+ 'oldest':'added_at',
+ 'votes':'-score'
+ }[sort_method]
+ )
+ #now put those answers first
+ for answer in reversed(published_answers):
+ answers.remove(answer)
+ answers.insert(0, answer)
+ published_answer_ids.append(answer.id)
+
+ return (question_post, answers, post_to_author, published_answer_ids)
def has_accepted_answer(self):
return self.accepted_answer_id != None
@@ -814,6 +956,16 @@ class Thread(models.Model):
return self.followed_by.filter(id = user.id).count() > 0
return False
+ def is_moderated(self):
+ """True, if tread has SHOW_PUBLISHED_RESPONSES
+ group memberships"""
+ if askbot_settings.GROUPS_ENABLED:
+ return ThreadToGroup.objects.filter(
+ thread=self,
+ visibility=ThreadToGroup.SHOW_PUBLISHED_RESPONSES
+ )
+ return False
+
def add_child_posts_to_groups(self, groups):
"""adds questions and answers of the thread to
given groups, comments are taken care of implicitly
@@ -833,17 +985,32 @@ class Thread(models.Model):
tag__id__in=group_ids
).delete()
- def add_to_groups(self, groups, recursive=False):
+ def add_to_groups(
+ self, groups, visibility=ThreadToGroup.SHOW_ALL_RESPONSES, recursive=False
+ ):
"""adds thread to a list of groups
``groups`` argument may be any iterable of groups
"""
- self.groups.add(*groups)
+ for group in groups:
+ #todo: change to bulk create when django 1.3 goes out of use
+ thread_group, created = ThreadToGroup.objects.get_or_create(
+ thread=self,
+ group=group
+ )
+
+ if thread_group.visibility != visibility:
+ thread_group.visibility = visibility
+ thread_group.save()
+
if recursive == True:
#comments are taken care of automatically
self.add_child_posts_to_groups(groups)
def remove_from_groups(self, groups, recursive=False):
- self.groups.remove(*groups)
+ thread_groups = ThreadToGroup.objects.filter(
+ thread=self, group__in=groups
+ )
+ thread_groups.delete()
if recursive == True:
self.remove_child_posts_from_groups(groups)
@@ -860,7 +1027,7 @@ class Thread(models.Model):
The add by ID now only works if user belongs to that group
"""
if group_id:
- group = Tag.group_tags.get(id=group_id)
+ group = Group.objects.get(id=group_id)
groups = [group]
self.add_to_groups(groups)
@@ -972,6 +1139,8 @@ class Thread(models.Model):
#todo: factor out - tell author about suggested tags
suggested_tags = filter_suggested_tags(added_tags)
if len(suggested_tags) > 0:
+ #1) notify author that the tag is going to be moderated
+ #todo: factor this out
if len(suggested_tags) == 1:
msg = _(
'Tag %s is new and will be submitted for the '
@@ -983,6 +1152,7 @@ class Thread(models.Model):
'moderators approval'
) % ', '.join([tag.name for tag in suggested_tags])
user.message_set.create(message = msg)
+ #2) todo: notify moderators about newly suggested tags
####################################################################
self.update_summary_html() # regenerate question/thread summary html
diff --git a/askbot/models/tag.py b/askbot/models/tag.py
index 7f5126c8..4b635a2f 100644
--- a/askbot/models/tag.py
+++ b/askbot/models/tag.py
@@ -17,15 +17,11 @@ def get_global_group():
#revert the values
#todo: change groups to django groups
group_name = askbot_settings.GLOBAL_GROUP_NAME
+ from askbot.models import Group
try:
- return Tag.group_tags.get(name=group_name)
- except Tag.DoesNotExist:
- from askbot.models import get_admin
- return Tag.group_tags.get_or_create(
- group_name=group_name,
- user=get_admin(),
- is_open=False
- )
+ return Group.objects.get(name=group_name)
+ except Group.DoesNotExist:
+ return Group.objects.create(name=group_name)
def delete_tags(tags):
"""deletes tags in the list"""
@@ -89,7 +85,7 @@ def separate_unused_tags(tags):
return used, unused
def tags_match_some_wildcard(tag_names, wildcard_tags):
- """Same as
+ """Same as
:meth:`~askbot.models.tag.TagQuerySet.tags_match_some_wildcard`
except it works on tag name strings
"""
@@ -190,6 +186,10 @@ class TagManager(BaseQuerySetManager):
def get_query_set(self):
return TagQuerySet(self.model)
+ def get_content_tags(self):
+ """temporary function that filters out the group tags"""
+ return self.all()
+
def create(self, name = None, created_by = None, **kwargs):
"""Creates a new tag"""
if created_by.can_create_tags() or is_preapproved_tag_name(name):
@@ -265,7 +265,6 @@ class TagManager(BaseQuerySetManager):
created_tags.append(tag)
for tag_name in set(tag_names) - set(pre_suggested_tag_names):
-
#status for the new tags is automatically set within the create()
new_tag = Tag.objects.create(name = tag_name, created_by = user)
created_tags.append(new_tag)
@@ -273,64 +272,14 @@ class TagManager(BaseQuerySetManager):
if new_tag.status == Tag.STATUS_SUGGESTED:
new_tag.suggested_by.add(user)
- #todo: here we have a chance to send a signal and notify
- #whoever wants about the new tag creation
-
return created_tags
-class GroupTagQuerySet(TagQuerySet):
- """Custom query set for the group"""
-
- def get_for_user(self, user=None, private=False):
- if private:
- global_group = get_global_group()
- return self.filter(
- user_memberships__user=user
- ).exclude(id=global_group.id)
- else:
- return self.filter(user_memberships__user = user)
-
- 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))
-
-
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(BaseQuerySetManager):
- """manager for group tags"""
-
- def get_query_set(self):
- return GroupTagQuerySet(self.model)
-
- def get_or_create(self, group_name = None, user = None, is_open=True):
- """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:
- #iexact is important!!! b/c we don't want case variants
- #of tags
- tag = self.get(name__iexact = 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, is_open=is_open)
- group_profile.save()
- return tag
-
class Tag(models.Model):
#a couple of status constants
STATUS_SUGGESTED = 0
@@ -360,7 +309,6 @@ class Tag(models.Model):
)
objects = TagManager()
- group_tags = GroupTagManager()
class Meta:
app_label = 'askbot'
@@ -384,7 +332,8 @@ class MarkedTag(models.Model):
app_label = 'askbot'
def get_groups():
- return Tag.group_tags.get_all()
+ from askbot.models import Group
+ return Group.objects.all()
def get_group_names():
#todo: cache me
diff --git a/askbot/models/user.py b/askbot/models/user.py
index 14c1d189..8314b28a 100644
--- a/askbot/models/user.py
+++ b/askbot/models/user.py
@@ -6,6 +6,7 @@ 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.contrib.auth.models import Group as AuthGroup
from django.core import exceptions
from django.forms import EmailField, URLField
from django.utils.translation import ugettext as _
@@ -13,8 +14,11 @@ from django.utils.html import strip_tags
from askbot import const
from askbot.conf import settings as askbot_settings
from askbot.utils import functions
-from askbot.models.tag import Tag
+from askbot.models.base import BaseQuerySetManager
+from askbot.models.tag import Tag, get_global_group
+from askbot.models.tag import clean_group_name#todo - delete this
from askbot.forms import DomainNameField
+from askbot.utils.forms import email_is_allowed
class ResponseAndMentionActivityManager(models.Manager):
def get_query_set(self):
@@ -206,10 +210,7 @@ class Activity(models.Model):
return user_qs[0]
def get_snippet(self, max_length = 120):
- if self.summary == '':
- return self.content_object.get_snippet(max_length)
- else:
- return self.summary
+ return self.content_object.get_snippet(max_length)
def get_absolute_url(self):
return self.content_object.get_absolute_url()
@@ -340,28 +341,119 @@ class EmailFeedSetting(models.Model):
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
+class AuthUserGroups(models.Model):
+ """explicit model for the auth_user_groups bridge table.
"""
- group = models.ForeignKey(Tag, related_name = 'user_memberships')
- user = models.ForeignKey(User, related_name = 'group_memberships')
+ group = models.ForeignKey(AuthGroup)
+ user = models.ForeignKey(User)
class Meta:
- app_label = 'askbot'
+ app_label = 'auth'
unique_together = ('group', 'user')
+ db_table = 'auth_user_groups'
+ managed = False
+
+
+class GroupMembership(AuthUserGroups):
+ """contains one-to-one relation to ``auth_user_group``
+ and extra membership profile fields"""
+ #note: this may hold info on when user joined, etc
+ NONE = -1#not part of the choices as for this records should be just missing
+ PENDING = 0
+ FULL = 1
+ LEVEL_CHOICES = (#'none' is by absence of membership
+ (PENDING, 'pending'),
+ (FULL, 'full')
+ )
+ ALL_LEVEL_CHOICES = LEVEL_CHOICES + ((NONE, 'none'),)
-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)
+ level = models.SmallIntegerField(
+ default=FULL,
+ choices=LEVEL_CHOICES,
+ )
+
+
+ class Meta:
+ app_label = 'askbot'
+
+ @classmethod
+ def get_level_value_display(cls, level):
+ """returns verbose value given a numerical value
+ includes the "fanthom" NONE
+ """
+ values_dict = dict(cls.ALL_LEVEL_CHOICES)
+ return values_dict[level]
+
+
+class GroupQuerySet(models.query.QuerySet):
+ """Custom query set for the group"""
+
+ def get_for_user(self, user=None, private=False):
+ if private:
+ global_group = get_global_group()
+ return self.filter(
+ user=user
+ ).exclude(id=global_group.id)
+ else:
+ return self.filter(user = user)
+
+ def get_by_name(self, group_name = None):
+ return self.get(name = clean_group_name(group_name))
+
+
+class GroupManager(BaseQuerySetManager):
+ """model manager for askbot groups"""
+
+ def get_query_set(self):
+ return GroupQuerySet(self.model)
+
+ def create(self, **kwargs):
+ name = kwargs['name']
+ try:
+ group_ptr = AuthGroup.objects.get(name=name)
+ kwargs['group_ptr'] = group_ptr
+ except AuthGroup.DoesNotExist:
+ pass
+ return super(GroupManager, self).create(**kwargs)
+
+ def get_or_create(self, name = None, user = None, openness=None):
+ """creates a group tag or finds one, if exists"""
+ #todo: here we might fill out the group profile
+ try:
+ #iexact is important!!! b/c we don't want case variants
+ #of tags
+ group = self.get(name__iexact = name)
+ except self.model.DoesNotExist:
+ if openness is None:
+ group = self.create(name=name)
+ else:
+ group = self.create(name=name, openness=openness)
+ return group
+
+
+class Group(AuthGroup):
+ """group profile for askbot"""
+ OPEN = 0
+ MODERATED = 1
+ CLOSED = 2
+ OPENNESS_CHOICES = (
+ (OPEN, 'open'),
+ (MODERATED, 'moderated'),
+ (CLOSED, 'closed'),
+ )
+ logo_url = models.URLField(null=True)
+ description = models.OneToOneField(
+ 'Post', related_name='described_group',
+ null=True, blank=True
+ )
+ moderate_email = models.BooleanField(default=True)
+ moderate_answers_to_enquirers = models.BooleanField(
+ default=False,
+ help_text='If true, answers to outsiders questions '
+ 'will be shown to the enquirers only when '
+ 'selected by the group moderators.'
+ )
+ openness = models.SmallIntegerField(default=CLOSED, choices=OPENNESS_CHOICES)
#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(
@@ -372,37 +464,65 @@ class GroupProfile(models.Model):
null = True, blank = True, default = ''
)
+ objects = GroupManager()
+
class Meta:
- #added to make account merges work properly
app_label = 'askbot'
-
- def can_accept_user(self, user):
- """True if user is preapproved to join the group"""
+ db_table = 'askbot_group'
+
+ def get_moderators(self):
+ """returns group moderators"""
+ user_filter = models.Q(is_superuser=True) | models.Q(status='m')
+ user_filter = user_filter & models.Q(groups__in=[self])
+ return User.objects.filter(user_filter)
+
+ def has_moderator(self, user):
+ """true, if user is a group moderator"""
+ mod_ids = self.get_moderators().values_list('id', flat=True)
+ return user.id in mod_ids
+
+ def get_openness_choices(self):
+ """gives answers to question
+ "How can users join this group?"
+ """
+ return (
+ (Group.OPEN, _('Can join when they want')),
+ (Group.MODERATED, _('Users ask permission')),
+ (Group.CLOSED, _('Moderator adds users'))
+ )
+
+ def get_openness_level_for_user(self, user):
+ """returns descriptive value, because it is to be used in the
+ templates. The value must match the verbose versions of the
+ openness choices!!!
+ """
if user.is_anonymous():
- return False
+ return 'closed'
#a special case - automatic global group cannot be joined or left
- if self.group_tag.name == askbot_settings.GLOBAL_GROUP_NAME:
- return False
+ if self.name == askbot_settings.GLOBAL_GROUP_NAME:
+ return 'closed'
- if self.is_open:
- return True
+ #todo - return 'closed' for internal per user groups too
+
+ if self.openness == Group.OPEN:
+ return 'open'
if user.is_administrator_or_moderator():
- return True
+ return 'open'
#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 email_is_allowed(
+ user.email,
+ allowed_emails=self.preapproved_emails,
+ allowed_email_domains=self.preapproved_email_domains
+ ):
+ return 'open'
- 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)
+ if self.openness == Group.MODERATED:
+ return 'moderated'
- return False
+ return 'closed'
def clean(self):
"""called in `save()`
@@ -429,4 +549,4 @@ class GroupProfile(models.Model):
def save(self, *args, **kwargs):
self.clean()
- super(GroupProfile, self).save(*args, **kwargs)
+ super(Group, self).save(*args, **kwargs)
diff --git a/askbot/models/widgets.py b/askbot/models/widgets.py
new file mode 100644
index 00000000..27fc3d84
--- /dev/null
+++ b/askbot/models/widgets.py
@@ -0,0 +1,38 @@
+from django.db import models
+from django.utils.translation import ugettext as _
+from askbot.models import Tag, Group
+from askbot.const import DEFAULT_QUESTION_WIDGET_STYLE, SEARCH_ORDER_BY
+
+class AskWidget(models.Model):
+ '''stores widgets styles and options'''
+ title = models.CharField(max_length=100)
+ group = models.ForeignKey(Group, null=True, blank=True)
+ tag = models.ForeignKey(Tag, null=True, blank=True)
+
+ include_text_field = models.BooleanField(default=False, blank=True)
+
+ inner_style = models.TextField(blank=True)
+ outer_style = models.TextField(blank=True)
+
+ class Meta:
+ app_label = 'askbot'
+
+ def __unicode__(self):
+ return "Widget: %s" % self.title
+
+
+class QuestionWidget(models.Model):
+ title = models.CharField(max_length=100)
+ question_number = models.PositiveIntegerField(default=7)
+ tagnames = models.CharField(_('tags'), max_length=50)
+ group = models.ForeignKey(Group, null=True, blank=True)
+ search_query = models.CharField(
+ max_length=50, null=True, blank=True, default=''
+ )
+ order_by = models.CharField(max_length=18,
+ choices=SEARCH_ORDER_BY, default='-added_at')
+ style = models.TextField(_('css for the widget'),
+ default=DEFAULT_QUESTION_WIDGET_STYLE, blank=True)
+
+ class Meta:
+ app_label = 'askbot'
diff --git a/askbot/search/postgresql/user_profile_search_08312012.plsql b/askbot/search/postgresql/user_profile_search_08312012.plsql
new file mode 100644
index 00000000..90b73148
--- /dev/null
+++ b/askbot/search/postgresql/user_profile_search_08312012.plsql
@@ -0,0 +1,93 @@
+/*
+Script depends on functions defined for general askbot full text search.
+to_tsvector(), add_tsvector_column()
+
+calculates text search vector for the user profile
+the searched fields are:
+1) user name
+2) user profile
+3) group names - for groups to which user belongs
+*/
+CREATE OR REPLACE FUNCTION get_auth_user_tsv(user_id integer)
+RETURNS tsvector AS
+$$
+DECLARE
+ group_query text;
+ user_query text;
+ onerow record;
+ tsv tsvector;
+BEGIN
+ group_query =
+ 'SELECT user_group.name as group_name ' ||
+ 'FROM auth_group AS user_group ' ||
+ 'INNER JOIN auth_user_groups AS gm ' ||
+ 'ON gm.user_id= ' || user_id || ' AND gm.group_id=user_group.id';
+
+ tsv = to_tsvector('');
+ FOR onerow in EXECUTE group_query LOOP
+ tsv = tsv || to_tsvector(onerow.group_name);
+ END LOOP;
+
+ user_query = 'SELECT username, about FROM auth_user WHERE id=' || user_id;
+ FOR onerow in EXECUTE user_query LOOP
+ tsv = tsv || to_tsvector(onerow.username) || to_tsvector(onerow.about);
+ END LOOP;
+ RETURN tsv;
+END;
+$$ LANGUAGE plpgsql;
+
+/* create tsvector columns in the content tables */
+SELECT add_tsvector_column('text_search_vector', 'auth_user');
+
+/* populate tsvectors with data */
+UPDATE auth_user SET text_search_vector = get_auth_user_tsv(id);
+
+/* one trigger per table for tsv updates */
+
+/* set up auth_user triggers */
+CREATE OR REPLACE FUNCTION auth_user_tsv_update_handler()
+RETURNS trigger AS
+$$
+BEGIN
+ new.text_search_vector = get_auth_user_tsv(new.id);
+ RETURN new;
+END;
+$$ LANGUAGE plpgsql;
+DROP TRIGGER IF EXISTS auth_user_tsv_update_trigger ON auth_user;
+
+CREATE TRIGGER auth_user_tsv_update_trigger
+BEFORE INSERT OR UPDATE ON auth_user
+FOR EACH ROW EXECUTE PROCEDURE auth_user_tsv_update_handler();
+
+/* group membership trigger - reindex users when group membership
+ * changes */
+CREATE OR REPLACE FUNCTION group_membership_tsv_update_handler()
+RETURNS trigger AS
+$$
+DECLARE
+ tsv tsvector;
+ user_query text;
+BEGIN
+ user_query = 'UPDATE auth_user SET username=username WHERE ' ||
+ 'id=' || new.user_id;
+ /* just trigger the tsv update on user */
+ EXECUTE user_query;
+ RETURN new;
+END;
+$$ LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS group_membership_tsv_update_trigger
+ON auth_user_groups;
+
+CREATE TRIGGER group_membership_tsv_update_trigger
+AFTER INSERT OR DELETE
+ON auth_user_groups
+FOR EACH ROW EXECUTE PROCEDURE group_membership_tsv_update_handler();
+
+/* todo: whenever group name changes - also
+ * reindex users belonging to the group */
+
+DROP INDEX IF EXISTS auth_user_search_idx;
+
+CREATE INDEX auth_user_search_idx ON auth_user
+USING gin(text_search_vector);
diff --git a/askbot/setup_templates/settings.py b/askbot/setup_templates/settings.py
index 7e65c833..0af52df1 100644
--- a/askbot/setup_templates/settings.py
+++ b/askbot/setup_templates/settings.py
@@ -150,6 +150,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
INSTALLED_APPS = (
+ 'longerusername',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
@@ -248,13 +249,12 @@ TINYMCE_DEFAULT_CONFIG = {
'plugins': 'askbot_imageuploader,askbot_attachment',
'theme_advanced_toolbar_location' : 'top',
'theme_advanced_toolbar_align': 'left',
- 'theme_advanced_buttons1': 'bold,italic,underline,|,bullist,numlist,|,undo,redo,|,link,unlink,askbot_imageuploader,askbot_attachment'
+ 'theme_advanced_buttons1': 'bold,italic,underline,|,bullist,numlist,|,undo,redo,|,link,unlink,askbot_imageuploader,askbot_attachment',
'theme_advanced_buttons2': '',
'theme_advanced_buttons3' : '',
'theme_advanced_path': False,
'theme_advanced_resizing': True,
'theme_advanced_resize_horizontal': False,
'theme_advanced_statusbar_location': 'bottom',
- 'width': '723',
'height': '250'
}
diff --git a/askbot/setup_templates/settings.py.mustache b/askbot/setup_templates/settings.py.mustache
index 56b15da0..82ff1d92 100644
--- a/askbot/setup_templates/settings.py.mustache
+++ b/askbot/setup_templates/settings.py.mustache
@@ -149,6 +149,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
INSTALLED_APPS = (
+ 'longerusername',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
@@ -249,7 +250,7 @@ TINYMCE_DEFAULT_CONFIG = {
'plugins': 'askbot_imageuploader,askbot_attachment',
'theme_advanced_toolbar_location' : 'top',
'theme_advanced_toolbar_align': 'left',
- 'theme_advanced_buttons1': 'bold,italic,underline,|,bullist,numlist,|,undo,redo,|,link,unlink,askbot_imageuploader,askbot_attachment'
+ 'theme_advanced_buttons1': 'bold,italic,underline,|,bullist,numlist,|,undo,redo,|,link,unlink,askbot_imageuploader,askbot_attachment',
'theme_advanced_buttons2': '',
'theme_advanced_buttons3' : '',
'theme_advanced_path': False,
diff --git a/askbot/setup_templates/tinymce_sample_config.py b/askbot/setup_templates/tinymce_sample_config.py
new file mode 100644
index 00000000..c75170b0
--- /dev/null
+++ b/askbot/setup_templates/tinymce_sample_config.py
@@ -0,0 +1,26 @@
+TINYMCE_COMPRESSOR = True
+TINYMCE_SPELLCHECKER = False
+TINYMCE_JS_ROOT = os.path.join(STATIC_ROOT, 'common/media/js/tinymce/')
+TINYMCE_URL = STATIC_URL + 'common/media/js/tinymce/'
+TINYMCE_DEFAULT_CONFIG = {
+ 'plugins': 'askbot_imageuploader,askbot_attachment',
+ 'theme': 'advanced',
+ 'content_css': STATIC_URL + 'default/media/style/tinymce/content.css',
+ 'force_br_newlines': True,
+ 'force_p_newlines': False,
+ 'forced_root_block': '',
+ 'mode' : 'textareas',
+ 'oninit': "function(){ tinyMCE.activeEditor.setContent(askbot['data']['editorContent'] || ''); }",
+ 'plugins': 'askbot_imageuploader,askbot_attachment',
+ 'theme_advanced_toolbar_location' : 'top',
+ 'theme_advanced_toolbar_align': 'left',
+ 'theme_advanced_buttons1': 'bold,italic,underline,|,bullist,numlist,|,undo,redo,|,link,unlink,askbot_imageuploader,askbot_attachment',
+ 'theme_advanced_buttons2': '',
+ 'theme_advanced_buttons3' : '',
+ 'theme_advanced_path': False,
+ 'theme_advanced_resizing': True,
+ 'theme_advanced_resize_horizontal': False,
+ 'theme_advanced_statusbar_location': 'bottom',
+ 'width': '723',
+ 'height': '250'
+}
diff --git a/askbot/skins/common/media/images/sprites.png b/askbot/skins/common/media/images/sprites.png
index e7244673..c372f9fa 100644
--- a/askbot/skins/common/media/images/sprites.png
+++ b/askbot/skins/common/media/images/sprites.png
Binary files differ
diff --git a/askbot/skins/common/media/js/autocompleter.js b/askbot/skins/common/media/js/autocompleter.js
index a7c54315..8121d2ea 100644
--- a/askbot/skins/common/media/js/autocompleter.js
+++ b/askbot/skins/common/media/js/autocompleter.js
@@ -10,6 +10,7 @@ var AutoCompleter = function(options) {
* Default options for autocomplete plugin
*/
var defaults = {
+ promptText: '',
autocompleteMultiple: true,
multipleSeparator: ' ',//a single character
inputClass: 'acInput',
@@ -147,6 +148,11 @@ AutoCompleter.prototype.decorate = function(element){
this._element.attr('autocomplete', 'off');
/**
+ * Set prompt text
+ */
+ this.setPrompt();
+
+ /**
* Create DOM element to hold results
*/
this._results = $('<div></div>').hide();
@@ -161,6 +167,21 @@ AutoCompleter.prototype.decorate = function(element){
this.setEventHandlers();
};
+AutoCompleter.prototype.setPrompt = function() {
+ this._element.val(this.options['promptText']);
+ this._element.addClass('prompt');
+};
+
+AutoCompleter.prototype.removePrompt = function() {
+ if (this._element.hasClass('prompt')) {
+ this._element.removeClass('prompt');
+ var val = this._element.val();
+ if (val === this.options['promptText']) {
+ this._element.val('');
+ }
+ }
+};
+
AutoCompleter.prototype.setEventHandlers = function(){
/**
* Shortcut to self
@@ -171,6 +192,9 @@ AutoCompleter.prototype.setEventHandlers = function(){
* Attach keyboard monitoring to $elem
*/
self._element.keydown(function(e) {
+
+ self.removePrompt();
+
self.lastKeyPressed_ = e.keyCode;
switch(self.lastKeyPressed_) {
@@ -204,6 +228,10 @@ AutoCompleter.prototype.setEventHandlers = function(){
break;
case 27: // escape
+ if ($.trim(self._element.val()) === '') {
+ self.setPrompt();
+ return false;
+ }
if (self.active_) {
e.preventDefault();
self.finish();
diff --git a/askbot/skins/common/media/js/live_search_new_thread.js b/askbot/skins/common/media/js/live_search_new_thread.js
index 53c820fa..eedd5fe8 100644
--- a/askbot/skins/common/media/js/live_search_new_thread.js
+++ b/askbot/skins/common/media/js/live_search_new_thread.js
@@ -1,5 +1,5 @@
-var liveSearchNewThreadInit = function() {
+var liveSearchNewThreadInit = function(auto_focus_out) {
var query = $('input#id_title.questionTitleInput');
var prev_text = $.trim(query.val());
var search_url = askbot['urls']['api_get_questions'];
@@ -15,16 +15,27 @@ var liveSearchNewThreadInit = function() {
}
});
+ query.focusout(function(){
+ if (auto_focus_out){
+ var restart_query_handle = setTimeout(restart_query, 500);
+ restart_query_handle();
+ }
+ });
+
+ var restart_query = function(){
+ /* restart query */
+ $('#' + q_list_sel).css('height',0).children().remove();
+ running = false;
+ prev_text = '';
+ }
+
var eval_query = function(){
cur_text = $.trim(query.val());
if (cur_text !== prev_text && running === false){
if (cur_text.length >= minSearchWordLength){
send_query(cur_text);
} else if (cur_text.length === 0){
- /* restart query */
- $('#' + q_list_sel).css('height',0).children().remove();
- running = false;
- prev_text = '';
+ restart_query();
}
}
};
@@ -43,6 +54,7 @@ var liveSearchNewThreadInit = function() {
list_item.append(count_element);
var link = $('<a></a>');
link.attr('href', url);
+ link.attr('target', '_blank');
list_item.append(link);
title_element = $('<span class="title"></span>');
title_element.html(title);
@@ -78,5 +90,4 @@ var liveSearchNewThreadInit = function() {
cache: false
});
}
-
};
diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js
index f74a6ce1..4c0dfa67 100644
--- a/askbot/skins/common/media/js/post.js
+++ b/askbot/skins/common/media/js/post.js
@@ -207,6 +207,65 @@ var CPValidator = function(){
/**
* @constructor
*/
+var ThreadUsersDialog = function() {
+ SimpleControl.call(this);
+ this._heading_text = 'Add heading with the setHeadingText()';
+};
+inherits(ThreadUsersDialog, SimpleControl);
+
+ThreadUsersDialog.prototype.setHeadingText = function(text) {
+ this._heading_text = text;
+};
+
+ThreadUsersDialog.prototype.showUsers = function(html) {
+ this._dialog.setContent(html);
+ this._dialog.show();
+};
+
+ThreadUsersDialog.prototype.startShowingUsers = function() {
+ var me = this;
+ var threadId = this._threadId;
+ var url = this._url;
+ $.ajax({
+ type: 'GET',
+ data: {'thread_id': threadId},
+ dataType: 'json',
+ url: url,
+ cache: false,
+ success: function(data){
+ if (data['success'] == true){
+ me.showUsers(data['html']);
+ } else {
+ showMessage(me.getElement(), data['message'], 'after');
+ }
+ }
+ });
+};
+
+ThreadUsersDialog.prototype.decorate = function(element) {
+ this._element = element;
+ ThreadUsersDialog.superClass_.decorate.call(this, element);
+ this._threadId = element.data('threadId');
+ this._url = element.data('url');
+ var dialog = new ModalDialog();
+ dialog.setRejectButtonText('');
+ dialog.setAcceptButtonText(gettext('Back to the question'));
+ dialog.setHeadingText(this._heading_text);
+ dialog.setAcceptHandler(function(){ dialog.hide(); });
+ var dialog_element = dialog.getElement();
+ $(dialog_element).find('.modal-footer').css('text-align', 'center');
+ $(document).append(dialog_element);
+ this._dialog = dialog;
+ var me = this;
+ this.setHandler(function(){
+ me.startShowingUsers();
+ });
+};
+
+
+/**
+ * @constructor
+ */
var DraftPost = function() {
WrappedElement.call(this);
};
@@ -270,7 +329,7 @@ DraftPost.prototype.decorate = function(element) {
this._element = element;
this.assignContentElements();
this.backupData();
- setInterval(this.getSaveHandler(), 5000);//auto-save twice a minute
+ setInterval(this.getSaveHandler(), 30000);//auto-save twice a minute
var me = this;
window.onbeforeunload = function() {
var saveHandler = me.getSaveHandler();
@@ -1606,6 +1665,8 @@ var Comment = function(widget, data){
this._data = data || {};
this._blank = true;//set to false by setContent
this._element = null;
+ this._is_convertible = askbot['data']['userIsAdminOrMod'];
+ this.convert_link = null;
this._delete_prompt = gettext('delete this comment');
if (data && data['is_deletable']){
this._deletable = data['is_deletable'];
@@ -1642,6 +1703,12 @@ Comment.prototype.decorate = function(element){
this._edit_link.decorate(edit_link);
}
+ var convert_link = this._element.find('form.convert-comment');
+ if (this._is_convertible){
+ this._convert_link = new CommentConvertLink(comment_id);
+ this._convert_link.decorate(convert_link);
+ }
+
var vote = new CommentVoteButton(this);
vote.decorate(this._element.find('.comment-votes .upvote'));
@@ -1725,6 +1792,11 @@ Comment.prototype.setContent = function(data){
this._edit_link.setHandler(this.getEditHandler())
this._comment_body.append(this._edit_link.getElement());
}
+
+ if (this._is_convertible){
+ this._convert_link = new CommentConvertLink(this._data['id']);
+ this._comment_body.append(this._convert_link.getElement());
+ }
this._element.append(this._comment_body);
this._blank = false;
@@ -1749,6 +1821,9 @@ Comment.prototype.dispose = function(){
if (this._edit_link){
this._edit_link.dispose();
}
+ if (this._convert_link){
+ this._convert_link.dispose();
+ }
this._data = null;
Comment.superClass_.dispose.call(this);
};
@@ -2102,10 +2177,6 @@ WMD.prototype.setPreviewerEnabled = function(state){
}
};
-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');
@@ -2152,12 +2223,74 @@ WMD.prototype.getText = function(){
WMD.prototype.start = function(){
Attacklab.Util.startEditor(true, this._enabled_buttons);
- this._textarea.keyup(makeKeyHandler(27, this._escape_handler));
};
/**
* @constructor
*/
+var TinyMCE = function(config) {
+ WrappedElement.call(this);
+ this._config = config || {};
+};
+inherits(TinyMCE, WrappedElement);
+
+/* 3 dummy functions to match WMD api */
+TinyMCE.prototype.setEnabledButtons = function() {};
+TinyMCE.prototype.start = function() {
+ this.loadEditor();
+};
+TinyMCE.prototype.setPreviewerEnabled = function() {};
+
+TinyMCE.prototype.setText = function(text) {
+ this._text = text;
+};
+
+TinyMCE.prototype.getText = function() {
+ return tinyMCE.activeEditor.getContent();
+};
+
+TinyMCE.prototype.loadEditor = function() {
+ var config = JSON.stringify(this._config);
+ var data = {config: config};
+ var editorBox = this._element;
+ var me = this;
+ $.ajax({
+ async: false,
+ type: 'GET',
+ dataType: 'json',
+ cache: false,
+ url: askbot['urls']['getEditor'],
+ data: data,
+ success: function(data) {
+ if (data['success']) {
+ editorBox.html(data['html']);
+ editorBox.find('textarea').val(me._text);//@todo: fixme
+ $.each(data['scripts'], function(idx, scriptData) {
+ var scriptElement = me.makeElement('script');
+ scriptElement.attr('type', 'text/javascript');
+ if (scriptData['src']) {
+ scriptElement.attr('src', scriptData['src']);
+ }
+ if (scriptData['contents']) {
+ scriptElement.html(scriptData['contents']);
+ }
+ $('head').append(scriptElement);
+ });
+ }
+ }
+ });
+};
+
+TinyMCE.prototype.createDom = function() {
+ var editorBox = this.makeElement('div');
+ editorBox.addClass('wmd-container');
+ this._element = editorBox;
+};
+
+/**
+ * @constructor
+ * @todo: change this to generic object description editor
+ */
var TagWikiEditor = function(){
WrappedElement.call(this);
this._state = 'display';//'edit' or 'display'
@@ -2231,10 +2364,14 @@ TagWikiEditor.prototype.setEditorLoaded = function(){
TagWikiEditor.prototype.startActivatingEditor = function(){
var editor = this._editor;
var me = this;
+ var data = {
+ object_id: me.getTagId(),
+ model_name: 'Group'
+ };
$.ajax({
type: 'GET',
- url: askbot['urls']['load_tag_wiki_text'],
- data: {tag_id: me.getTagId()},
+ url: askbot['urls']['load_object_description'],
+ data: data,
cache: false,
success: function(data){
me.backupContent();
@@ -2252,11 +2389,16 @@ TagWikiEditor.prototype.startActivatingEditor = function(){
TagWikiEditor.prototype.saveData = function(){
var me = this;
var text = this._editor.getText();
+ var data = {
+ object_id: me.getTagId(),
+ model_name: 'Group',//todo: fixme
+ text: text
+ };
$.ajax({
type: 'POST',
dataType: 'json',
- url: askbot['urls']['save_tag_wiki_text'],
- data: {tag_id: me.getTagId(), text: text},
+ url: askbot['urls']['save_object_description'],
+ data: data,
cache: false,
success: function(data){
if (data['success']){
@@ -2304,13 +2446,19 @@ TagWikiEditor.prototype.decorate = function(element){
if (askbot['settings']['editorType'] === 'markdown') {
var editor = new WMD();
} else {
- var editor = new TinyMCEWrapper();
+ var editor = new TinyMCE({//override defaults
+ mode: 'exact',
+ elements: 'editor',
+ theme_advanced_buttons1: 'bold, italic, |, link, |, numlist, bullist',
+ theme_advanced_buttons2: '',
+ plugins: '',
+ width: '200'
+ });
}
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()});
@@ -2498,13 +2646,21 @@ UserGroupProfileEditor.prototype.decorate = function(element){
this._moderate_email_btn = moderate_email_btn;
moderate_email_toggle.decorate(moderate_email_btn);
- var open_group_toggle = new TwoStateToggle();
- open_group_toggle.setPostData({
+ var moderate_publishing_replies_toggle = new TwoStateToggle();
+ moderate_publishing_replies_toggle.setPostData({
group_id: this.getTagId(),
- property_name: 'is_open'
+ property_name: 'moderate_answers_to_enquirers'
});
- var open_group_btn = element.find('#open-or-close-group');
- open_group_toggle.decorate(open_group_btn);
+ var btn = element.find('#moderate-answers-to-enquirers');
+ moderate_publishing_replies_toggle.decorate(btn);
+
+ var opennessSelector = new DropdownSelect();
+ var selectorElement = element.find('#group-openness-selector');
+ opennessSelector.setPostData({
+ group_id: this.getTagId(),
+ property_name: 'openness'
+ });
+ opennessSelector.decorate(selectorElement);
var email_editor = new TextPropertyEditor();
email_editor.decorate(element.find('#preapproved-emails'));
@@ -2548,7 +2704,11 @@ GroupJoinButton.prototype.getHandler = function(){
url: askbot['urls']['join_or_leave_group'],
success: function(data){
if (data['success']){
- var new_state = data['is_member'] ? 'on-state':'off-state';
+ var level = data['membership_level'];
+ var new_state = 'off-state';
+ if (level == 'full' || level == 'pending') {
+ new_state = 'on-state';
+ }
me.setState(new_state);
} else {
showMessage(me.getElement(), data['message']);
@@ -3813,6 +3973,27 @@ $(document).ready(function() {
deleter.setPostId(post_id);
deleter.decorate($(element).find('.question-delete'));
});
+ //todo: convert to "control" class
+ var publishBtns = $('.answer-publish, .answer-unpublish');
+ publishBtns.each(function(idx, btn) {
+ setupButtonEventHandlers($(btn), function() {
+ var answerId = $(btn).data('answerId');
+ $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ data: {'answer_id': answerId},
+ url: askbot['urls']['publishAnswer'],
+ success: function(data) {
+ if (data['success']) {
+ window.location.reload(true);
+ } else {
+ showMessage($(btn), data['message']);
+ }
+ }
+ });
+ });
+ });
+
if (askbot['settings']['tagSource'] == 'category-tree') {
var catSelectorLoader = new CategorySelectorLoader();
catSelectorLoader.decorate($('#retag'));
@@ -3824,8 +4005,6 @@ $(document).ready(function() {
var proxyUserNameInput = $('#id_post_author_username');
var proxyUserEmailInput = $('#id_post_author_email');
if (proxyUserNameInput.length === 1) {
- var tip = new TippedInput();
- tip.decorate(proxyUserNameInput);
var userSelectHandler = function(data) {
proxyUserEmailInput.val(data['data'][0]);
@@ -3834,6 +4013,7 @@ $(document).ready(function() {
var fakeUserAc = new AutoCompleter({
url: '/get-users-info/',//askbot['urls']['get_users_info'],
preloadData: true,
+ promptText: gettext('User name:'),
minChars: 1,
useCache: true,
matchInside: true,
@@ -3841,11 +4021,13 @@ $(document).ready(function() {
delay: 10,
onItemSelect: userSelectHandler
});
+
fakeUserAc.decorate(proxyUserNameInput);
- }
- if (proxyUserEmailInput.length === 1) {
- var tip = new TippedInput();
- tip.decorate(proxyUserEmailInput);
+ if (proxyUserEmailInput.length === 1) {
+ var tip = new TippedInput();
+ tip.decorate(proxyUserEmailInput);
+ }
+
}
//if groups are enabled - activate share functions
var groupsInput = $('#share_group_name');
@@ -3853,6 +4035,7 @@ $(document).ready(function() {
var groupsAc = new AutoCompleter({
url: askbot['urls']['getGroupsList'],
preloadData: true,
+ promptText: gettext('Group name:'),
minChars: 1,
useCache: false,
matchInside: true,
@@ -3866,6 +4049,7 @@ $(document).ready(function() {
var usersAc = new AutoCompleter({
url: '/get-users-info/',
preloadData: true,
+ promptText: gettext('User name:'),
minChars: 1,
useCache: false,
matchInside: true,
@@ -3874,6 +4058,19 @@ $(document).ready(function() {
});
usersAc.decorate(usersInput);
}
+
+ var showSharedUsers = $('.see-related-users');
+ if (showSharedUsers.length) {
+ var usersPopup = new ThreadUsersDialog();
+ usersPopup.setHeadingText(gettext('Shared with the following users:'));
+ usersPopup.decorate(showSharedUsers);
+ }
+ var showSharedGroups = $('.see-related-groups');
+ if (showSharedGroups.length) {
+ var groupsPopup = new ThreadUsersDialog();
+ groupsPopup.setHeadingText(gettext('Shared with the following groups:'));
+ groupsPopup.decorate(showSharedGroups);
+ }
});
diff --git a/askbot/skins/common/media/js/user.js b/askbot/skins/common/media/js/user.js
index 4795b7d2..2fd1195b 100644
--- a/askbot/skins/common/media/js/user.js
+++ b/askbot/skins/common/media/js/user.js
@@ -1,3 +1,4 @@
+//todo: refactor this into "Inbox" object or more specialized
var setup_inbox = function(){
var getSelected = function(){
@@ -114,15 +115,15 @@ var setup_inbox = function(){
var rejectPostDialog = new RejectPostDialog();
rejectPostDialog.decorate($('#reject-edit-modal'));
+ rejectPostDialog.setSelectedEditDataReader(function(){
+ return getSelected();
+ });
setupButtonEventHandlers(
$('#re_delete_post'),
function(){
- var data = getSelected();
- if (data['id_list'].length === 0){
- return;
+ if (rejectPostDialog.readSelectedEditData()) {
+ rejectPostDialog.show();
}
- rejectPostDialog.setSelectedEditData(data);
- rejectPostDialog.show();
}
);
@@ -136,15 +137,6 @@ var setup_inbox = function(){
$(response).append(control.getElement());
});
}
- //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(){
@@ -182,9 +174,19 @@ PostModerationControls.prototype.getMemoId = function() {
return this._parent_element.data('responseId');
};
-PostModerationControls.prototype.removeMemo = function() {
+PostModerationControls.prototype.getMemoElement = function() {
var reId = this.getMemoId();
- $('#re_' + reId).remove();
+ return $('#re_' + reId);
+};
+
+PostModerationControls.prototype.removeMemo = function() {
+ this.getMemoElement().remove();
+};
+
+PostModerationControls.prototype.markMemo = function() {
+ var memo = this.getMemoElement();
+ var checkbox = memo.find('input[type="checkbox"]');
+ checkbox.attr('checked', true);
};
PostModerationControls.prototype.addReason = function(id, title) {
@@ -277,7 +279,9 @@ PostModerationControls.prototype.createDom = function() {
var reasonsDlg = this._reasonsDialog;
setupButtonEventHandlers(anchor, function() {
- reasonsDlg.show();
+ me.markMemo();//mark current post
+ reasonsDlg.readSelectedEditData();//read data of selected edits
+ reasonsDlg.show();//open the "big" dialog
});
setupButtonEventHandlers(acceptBtn, function() {
me.moderatePost(null, 'remove_flag');
@@ -295,9 +299,20 @@ var RejectPostDialog = function(){
this._selected_reason_id = null;
this._state = null;//'select', 'preview', 'add-new'
this._postModerationControls = [];
+ this._selectedEditDataReader = undefined;
};
inherits(RejectPostDialog, WrappedElement);
+RejectPostDialog.prototype.setSelectedEditDataReader = function(func) {
+ this._selectedEditDataReader = func;
+};
+
+RejectPostDialog.prototype.readSelectedEditData = function() {
+ var data = this._selectedEditDataReader();
+ this.setSelectedEditData(data);
+ return data['id_list'].length > 0;
+};
+
RejectPostDialog.prototype.setSelectedEditData = function(data){
this._selected_edit_data = data;
};
@@ -485,7 +500,10 @@ RejectPostDialog.prototype.rejectPost = function(reason_id){
url: askbot['urls']['manageInbox'],
success: function(data){
if (data['success']){
- memos.remove();
+ $.each(memos, function(idx, memo) {
+ $(memo).next('.post-moderation-controls').remove();
+ $(memo).remove();
+ });
me.hide();
} else {
//only fatal errors here
diff --git a/askbot/skins/common/media/js/utils.js b/askbot/skins/common/media/js/utils.js
index 97ed7bf3..42f19d5b 100644
--- a/askbot/skins/common/media/js/utils.js
+++ b/askbot/skins/common/media/js/utils.js
@@ -249,20 +249,65 @@ var inherits = function(childCtor, parentCtor) {
childCtor.prototype.constructor = childCtor;
};
-/* wrapper around jQuery object */
+/** wrapper around jQuery object
+ * @constructor
+ * the top level "class" for other elements
+ * I.e. all other things must inherit this class.
+ * For an example of the inheritance pattern,
+ * please see the "TippedInput" below.
+ */
var WrappedElement = function(){
this._element = null;
this._in_document = false;
};
+/* note that we do not call inherits() here
+ * See TippedInput as an example of a subclass
+ */
+
+/**
+ * notice that we use ObjCls.prototype.someMethod = function()
+ * notation - as we use Javascript's prototypal inheritance
+ * explicitly. The point of this is to be able to eventually
+ * use the Closure Compiler
+ */
WrappedElement.prototype.setElement = function(element){
this._element = element;
};
+
+/**
+ * this function must be overridden for any object
+ * what will use "DOM generation" pattern
+ *
+ * Inside this function two things can happen:
+ * 1) dom structure creation
+ * 2) event handlers attached to the dom structure
+ */
WrappedElement.prototype.createDom = function(){
+ /* inside at the very least you must assign
+ * a jQuery object to a parameter called _element
+ */
this._element = $('<div></div>');
};
+
+/**
+ * @param {object} element, a jQuery object wrapping a single
+ * DOM element.
+ *
+ * This function must be overridden in the subclasses
+ * that are used in the "decoration" pattern
+ */
WrappedElement.prototype.decorate = function(element){
this._element = element;
};
+
+/**
+ * This method should not be overridden
+ * Normally you call this method to generate the dom
+ * structure, if applicable, or just obtain the
+ * jQuery object encapsulating the dom.
+ *
+ * @return {object} jQuery
+ */
WrappedElement.prototype.getElement = function(){
if (this._element === null){
this.createDom();
@@ -278,10 +323,23 @@ WrappedElement.prototype.enterDocument = function(){
WrappedElement.prototype.hasElement = function(){
return (this._element !== null);
};
+/**
+ * A utility method, returning a new jQuery object for
+ * some HTML tag
+ *
+ * Example:
+ * var ageInput = this.makeElement('input');
+ */
WrappedElement.prototype.makeElement = function(html_tag){
//makes jQuery element with tags
return $('<' + html_tag + '></' + html_tag + '>');
};
+/**
+ * Removes object's DOM element from the DOM tree
+ * should be overridden to remove the event handlers
+ * and properly destroy the dom structure
+ * as well as any other included sub-elements
+ */
WrappedElement.prototype.dispose = function(){
this._element.remove();
this._in_document = false;
@@ -320,14 +378,29 @@ Widget.prototype.makeButton = function(label, handler) {
* perhaps empty text, the instruction is restored.
* When instruction is shown, class "blank" is present
* in the input/textare element.
+ *
+ * For the usage examples - search for "new TippedInput"
+ * there is at least one example for both - decoration and
+ * the "dom creation" patterns.
+ *
+ * Also - in the FileUploadDialog the TippedInput is used
+ * as a sub-element - the widget composition use case.
*/
var TippedInput = function(){
+ /* the call below is part 1 of the inheritance pattern */
WrappedElement.call(this);
this._instruction = null;
this._attrs = {};
//this._is_one_shot = false;//if true on starting typing effect is gone
};
inherits(TippedInput, WrappedElement);
+/* the line above is part 2 of the inheritance pattern
+ see definition of the function "inherits" for more details
+*/
+
+/* Below are all the custom methods of the
+ TippedInput class, as well as some required functions
+*/
TippedInput.prototype.reset = function(){
$(this._element).val(this._instruction);
@@ -364,22 +437,48 @@ TippedInput.prototype.setVal = function(value){
}
}
};
-
+/**
+ * Creates the DOM of tipped input from scratch
+ * Notice that there is also a "decorate" method.
+ * At least one - createDom or decorate is required,
+ * depending on the usage.
+ */
TippedInput.prototype.createDom = function() {
this._element = this.makeElement('input');
var element = this._element;
element.val(this._instruction);
+
+ //here we re-use the decorate call (next method)
+ //to avoid copy-pasting code
this.decorate(element);
};
+/**
+ * Attaches the TippedInput behavior to
+ * a pre-existing <input> element
+ *
+ * decorate() method normally does not create
+ * new dom elements, but it might add some missing elements,
+ * if necessary.
+ *
+ * for example the decorate might be composing inside
+ * a more complex widget, in which case other elements
+ * can be added via a "composition" pattern, or
+ * just "naked dom elements" added to the current widget's element
+ *
+ */
TippedInput.prototype.decorate = function(element){
- this._element = element;
+ this._element = element;//mandatory line
+
+ //part 1 - initialize some values and create dom
element.attr(this._attrs);
var instruction_text = this.getVal();
this._instruction = instruction_text;
this.reset();
var me = this;
+
+ //part 2 - attach event handlers
$(element).focus(function(){
if (me.isBlank()){
$(element)
@@ -395,9 +494,11 @@ TippedInput.prototype.decorate = function(element){
.addClass('blank');
}
});
- makeKeyHandler(13, function(){
- $(element).blur();
- });
+ $(element).keydown(
+ makeKeyHandler(27, function(){
+ $(element).blur();
+ })
+ );
};
/**
@@ -554,6 +655,36 @@ EditLink.prototype.decorate = function(element){
this.setHandlerInternal();
};
+var CommentConvertLink = function(comment_id){
+ WrappedElement.call(this)
+ this._comment_id = comment_id;
+};
+inherits(CommentConvertLink, WrappedElement);
+
+CommentConvertLink.prototype.createDom = function(){
+ var element = this.makeElement('form');
+ element.addClass('convert-comment');
+ element.attr('method', 'POST');
+ element.attr('action', askbot['urls']['convertComment']);
+ var hidden_input = this.makeElement('input');
+ hidden_input.attr('type', 'hidden');
+ hidden_input.attr('value', this._comment_id);
+ hidden_input.attr('name', 'comment_id');
+ hidden_input.attr('id', 'id_comment_id');
+ element.append(hidden_input);
+
+ var submit = this.makeElement('input');
+ submit.attr('type', 'submit');
+ submit.attr('value', gettext('convert to answer'));
+ element.append(submit);
+ this.decorate(element);
+};
+
+
+CommentConvertLink.prototype.decorate = function(element){
+ this._element = element;
+};
+
var DeleteIcon = function(title){
SimpleControl.call(this);
this._title = title;
@@ -618,6 +749,9 @@ ModalDialog.prototype.hide = function() {
ModalDialog.prototype.setContent = function(content) {
this._initial_content = content;
+ if (this._content_element) {
+ this._content_element.html(content);
+ }
};
ModalDialog.prototype.prependContent = function(content) {
@@ -697,14 +831,18 @@ ModalDialog.prototype.createDom = function() {
accept_btn.html(this._accept_button_text);
footer.append(accept_btn);
- var reject_btn = this.makeElement('button');
- reject_btn.addClass('btn cancel');
- reject_btn.html(this._reject_button_text);
- footer.append(reject_btn);
+ if (this._reject_button_text) {
+ var reject_btn = this.makeElement('button');
+ reject_btn.addClass('btn cancel');
+ reject_btn.html(this._reject_button_text);
+ footer.append(reject_btn);
+ }
//4) attach event handlers to the buttons
setupButtonEventHandlers(accept_btn, this._accept_handler);
- setupButtonEventHandlers(reject_btn, this._reject_handler);
+ if (this._reject_button_text) {
+ setupButtonEventHandlers(reject_btn, this._reject_handler);
+ }
setupButtonEventHandlers(close_link, this._reject_handler);
this.hide();
@@ -1154,6 +1292,52 @@ TwoStateToggle.prototype.decorate = function(element){
setupButtonEventHandlers(element, this.getHandler());
};
+/**
+ * @contstructor
+ * a simple dropdown select element
+ * which saves data to the server on change
+ */
+var DropdownSelect = function() {
+ WrappedElement.call(this);
+};
+inherits(DropdownSelect, WrappedElement);
+
+DropdownSelect.prototype.setPostData = function(data) {
+ this._postData = data;
+};
+
+/**
+ * posts value of selection to the url given
+ * with data-url and parameter called "value"
+ */
+DropdownSelect.prototype.saveChoice = function() {
+ var element = this._element;
+ var url = this._url;
+ var data = this._postData;
+ data['value'] = element.val();
+ $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ data: data,
+ cache: false,
+ url: url,
+ success: function(data) {
+ if (!data['success']) {
+ showMessage(element, data['message']);
+ }
+ }
+ });
+};
+
+DropdownSelect.prototype.decorate = function(element) {
+ this._element = element;
+ this._url = $(element).data('url');
+ var me = this;
+ this._element.change(function() {
+ me.saveChoice();
+ });
+};
+
var BoxItemContent = function() {
SimpleContent.call(this);
};
@@ -1449,6 +1633,122 @@ SelectBox.prototype.decorate = function(element){
});
};
+/**
+ * This is a dropdown list elment
+ */
+
+var GroupDropdown = function(groups){
+ WrappedElement.call(this);
+ this._group_list = groups;
+ this._input_box = new TippedInput();
+ this._input_box.setInstruction('group name');
+ this._input_box.createDom();
+ this._input_box_element = this._input_box.getElement();
+ this._input_box_element.attr('class', 'group-name');
+ this._input_box_element.hide();
+ this._add_link = this.makeElement('a');
+ this._add_link.attr('href', '#');
+ this._add_link.attr('class', 'group-name');
+ this._add_link.text('add new group');
+};
+inherits(GroupDropdown, WrappedElement);
+
+GroupDropdown.prototype.createDom = function(){
+ this._element = this.makeElement('ul');
+ this._element.attr('class', 'dropdown-menu');
+ this._element.attr('id', 'groups-dropdown');
+ this._element.attr('role', 'menu');
+ this._element.attr('aria-labelledby', 'navGroups');
+
+ for (i=0; i<this._group_list.length; i++){
+ li_element = this.makeElement('li');
+ a_element = this.makeElement('a');
+ a_element.text(this._group_list[i].name);
+ a_element.attr('href', this._group_list[i].link);
+ a_element.attr('class', 'group-name');
+ li_element.append(a_element);
+ this._element.append(li_element);
+ }
+};
+
+GroupDropdown.prototype.decorate = function(element){
+ this._element = element;
+ this._element.attr('class', 'dropdown-menu');
+ this._element.attr('id', 'groups-dropdown');
+ this._element.attr('role', 'menu');
+ this._element.attr('aria-labelledby', 'navGroups');
+
+ for (i=0; i<this._group_list.length; i++){
+ li_element = this.makeElement('li');
+ a_element = this.makeElement('a');
+ a_element.text(this._group_list[i].name);
+ a_element.attr('href', this._group_list[i].link);
+ a_element.attr('class', 'group-name');
+ li_element.append(a_element);
+ this._element.append(li_element);
+ }
+};
+
+GroupDropdown.prototype.prependGroup = function(group_name, url){
+ new_group_li = this.makeElement('li');
+ new_group_a = this.makeElement('a');
+ new_group_a.attr('href', url);
+ new_group_a.attr('class', 'group-name');
+ new_group_a.text(group_name);
+ new_group_li.append(new_group_a);
+ this._element.prepend(new_group_li);
+};
+
+GroupDropdown.prototype._add_group_handler = function(group_name){
+ var group_name = this._input_box_element.val();
+ self = this;
+ if (!group_name){
+ return;
+ }
+
+ $.ajax({
+ type: 'POST',
+ url: askbot['urls']['add_group'],
+ data: {group: group_name},
+ success: function(data){
+ if (data.success){
+ self.prependGroup(data.group_name, data.url);
+ self._input_box_element.hide();
+ self._add_link.show();
+ return true;
+ } else{
+ return false;
+ }
+ },
+ error: function(){console.log('error');}
+ });
+};
+
+GroupDropdown.prototype.enableAddGroups = function(){
+ var self = this;
+ this._add_link.click(function(){
+ self._add_link.hide();
+ self._input_box_element.show();
+ self._input_box_element.focus();
+ });
+ this._input_box_element.keydown(function(event){
+ if (event.which == 13 || event.keyCode==13){
+ self._add_group_handler();
+ self._input_box_element.val('');
+ }
+ });
+
+ var divider = this.makeElement('li');
+ divider.attr('class', 'divider');
+ this._element.append(divider);
+
+ var container = this.makeElement('li');
+ container.append(this._add_link);
+ container.append(this._input_box_element);
+
+ this._element.append(container);
+};
+
var Tag = function(){
SimpleControl.call(this);
this._deletable = false;
@@ -1610,8 +1910,808 @@ Hilite={elementid:"content",exact:true,max_nodes:1000,onload:true,style_name:"hi
if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());
//jquery fieldselection
(function(){var a={getSelection:function(){var b=this.jquery?this[0]:this;return(("selectionStart" in b&&function(){var c=b.selectionEnd-b.selectionStart;return{start:b.selectionStart,end:b.selectionEnd,length:c,text:b.value.substr(b.selectionStart,c)}})||(document.selection&&function(){b.focus();var d=document.selection.createRange();if(d==null){return{start:0,end:b.value.length,length:0}}var c=b.createTextRange();var e=c.duplicate();c.moveToBookmark(d.getBookmark());e.setEndPoint("EndToStart",c);return{start:e.text.length,end:e.text.length+d.text.length,length:d.text.length,text:d.text}})||function(){return{start:0,end:b.value.length,length:0}})()},replaceSelection:function(){var b=this.jquery?this[0]:this;var c=arguments[0]||"";return(("selectionStart" in b&&function(){b.value=b.value.substr(0,b.selectionStart)+c+b.value.substr(b.selectionEnd,b.value.length);return this})||(document.selection&&function(){b.focus();document.selection.createRange().text=c;return this})||function(){b.value+=c;return this})()}};jQuery.each(a,function(b){jQuery.fn[b]=this})})();
-//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)};
+/**
+ * AutoCompleter Object, refactored closure style from
+ * jQuery autocomplete plugin
+ * @param {Object=} options Settings
+ * @constructor
+ */
+var AutoCompleter = function(options) {
+
+ /**
+ * Default options for autocomplete plugin
+ */
+ var defaults = {
+ promptText: '',
+ autocompleteMultiple: true,
+ multipleSeparator: ' ',//a single character
+ 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, // TBD
+ onItemSelect: false,
+ autoFill: false,
+ filterResults: true,
+ sortResults: true,
+ sortFunction: false,
+ onNoMatch: false
+ };
+
+ /**
+ * Options dictionary
+ * @type Object
+ * @private
+ */
+ this.options = $.extend({}, defaults, options);
+
+ /**
+ * Cached data
+ * @type Object
+ * @private
+ */
+ this.cacheData_ = {};
+
+ /**
+ * Number of cached data items
+ * @type number
+ * @private
+ */
+ this.cacheLength_ = 0;
+
+ /**
+ * Class name to mark selected item
+ * @type string
+ * @private
+ */
+ this.selectClass_ = 'jquery-autocomplete-selected-item';
+
+ /**
+ * Handler to activation timeout
+ * @type ?number
+ * @private
+ */
+ this.keyTimeout_ = null;
+
+ /**
+ * Last key pressed in the input field (store for behavior)
+ * @type ?number
+ * @private
+ */
+ this.lastKeyPressed_ = null;
+
+ /**
+ * Last value processed by the autocompleter
+ * @type ?string
+ * @private
+ */
+ this.lastProcessedValue_ = null;
+
+ /**
+ * Last value selected by the user
+ * @type ?string
+ * @private
+ */
+ this.lastSelectedValue_ = null;
+
+ /**
+ * Is this autocompleter active?
+ * @type boolean
+ * @private
+ */
+ this.active_ = false;
+
+ /**
+ * Is it OK to finish on blur?
+ * @type boolean
+ * @private
+ */
+ 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(element){
+
+ /**
+ * Init DOM elements repository
+ */
+ this._element = element;
+
+ /**
+ * Switch off the native autocomplete
+ */
+ this._element.attr('autocomplete', 'off');
+
+ /**
+ * Set prompt text
+ */
+ this.setPrompt();
+
+ /**
+ * Create DOM element to hold results
+ */
+ 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.setPrompt = function() {
+ this._element.val(this.options['promptText']);
+ this._element.addClass('prompt');
+};
+
+AutoCompleter.prototype.removePrompt = function() {
+ if (this._element.hasClass('prompt')) {
+ this._element.removeClass('prompt');
+ var val = this._element.val();
+ if (val === this.options['promptText']) {
+ this._element.val('');
+ }
+ }
+};
+
+AutoCompleter.prototype.setEventHandlers = function(){
+ /**
+ * Shortcut to self
+ */
+ var self = this;
+
+ /**
+ * Attach keyboard monitoring to $elem
+ */
+ self._element.keydown(function(e) {
+
+ self.removePrompt();
+
+ self.lastKeyPressed_ = e.keyCode;
+ switch(self.lastKeyPressed_) {
+
+ case 38: // up
+ e.preventDefault();
+ if (self.active_) {
+ self.focusPrev();
+ } else {
+ self.activate();
+ }
+ return false;
+ break;
+
+ case 40: // down
+ e.preventDefault();
+ if (self.active_) {
+ self.focusNext();
+ } else {
+ self.activate();
+ }
+ return false;
+ break;
+
+ case 9: // tab
+ case 13: // return
+ if (self.active_) {
+ e.preventDefault();
+ self.selectCurrent();
+ return false;
+ }
+ break;
+
+ case 27: // escape
+ if ($.trim(self._element.val()) === '') {
+ self._element.blur();
+ return false;
+ }
+ if (self.active_) {
+ e.preventDefault();
+ self.finish();
+ return false;
+ }
+ break;
+
+ default:
+ self.activate();
+
+ }
+ });
+ self._element.focus(function() {
+ self.removePrompt();
+ });
+ self._element.blur(function() {
+ if ($.trim(self._element.val()) === '') {
+ self.setPrompt();
+ self._results.hide();
+ return true;
+ }
+ if (self.finishOnBlur_) {
+ setTimeout(function() { self.finish(); }, 200);
+ }
+ });
+};
+
+AutoCompleter.prototype.position = function() {
+ var offset = this._element.offset();
+ this._results.css({
+ top: offset.top + this._element.outerHeight(),
+ left: offset.left
+ });
+};
+
+AutoCompleter.prototype.cacheRead = function(filter) {
+ var filterLength, searchLength, search, maxPos, pos;
+ if (this.options.useCache) {
+ filter = String(filter);
+ filterLength = filter.length;
+ if (this.options.matchSubset) {
+ searchLength = 1;
+ } else {
+ searchLength = filterLength;
+ }
+ while (searchLength <= filterLength) {
+ if (this.options.matchInside) {
+ maxPos = filterLength - searchLength;
+ } else {
+ maxPos = 0;
+ }
+ pos = 0;
+ while (pos <= maxPos) {
+ search = filter.substr(0, searchLength);
+ if (this.cacheData_[search] !== undefined) {
+ return this.cacheData_[search];
+ }
+ pos++;
+ }
+ searchLength++;
+ }
+ }
+ return false;
+};
+
+AutoCompleter.prototype.cacheWrite = function(filter, data) {
+ if (this.options.useCache) {
+ if (this.cacheLength_ >= this.options.maxCacheLength) {
+ this.cacheFlush();
+ }
+ filter = String(filter);
+ if (this.cacheData_[filter] !== undefined) {
+ this.cacheLength_++;
+ }
+ return this.cacheData_[filter] = data;
+ }
+ return false;
+};
+
+AutoCompleter.prototype.cacheFlush = function() {
+ this.cacheData_ = {};
+ this.cacheLength_ = 0;
+};
+
+AutoCompleter.prototype.callHook = function(hook, data) {
+ var f = this.options[hook];
+ if (f && $.isFunction(f)) {
+ return f(data, this);
+ }
+ return false;
+};
+
+AutoCompleter.prototype.activate = function() {
+ var self = this;
+ var activateNow = function() {
+ self.activateNow();
+ };
+ var delay = parseInt(this.options.delay, 10);
+ if (isNaN(delay) || delay <= 0) {
+ delay = 250;
+ }
+ if (this.keyTimeout_) {
+ clearTimeout(this.keyTimeout_);
+ }
+ this.keyTimeout_ = setTimeout(activateNow, delay);
+};
+
+AutoCompleter.prototype.activateNow = function() {
+ var value = this.getValue();
+ if (value !== this.lastProcessedValue_ && value !== this.lastSelectedValue_) {
+ if (value.length >= this.options.minChars) {
+ this.active_ = true;
+ this.lastProcessedValue_ = value;
+ this.fetchData(value);
+ }
+ }
+};
+
+AutoCompleter.prototype.fetchData = function(value) {
+ if (this.options.data) {
+ this.filterAndShowResults(this.options.data, value);
+ } else {
+ var self = this;
+ this.fetchRemoteData(value, function(remoteData) {
+ self.filterAndShowResults(remoteData, value);
+ });
+ }
+};
+
+AutoCompleter.prototype.fetchRemoteData = function(filter, callback) {
+ var data = this.cacheRead(filter);
+ if (data) {
+ callback(data);
+ } else {
+ var self = this;
+ if (this._element){
+ this._element.addClass(this.options.loadingClass);
+ }
+ var ajaxCallback = function(data) {
+ var parsed = false;
+ if (data !== false) {
+ parsed = self.parseRemoteData(data);
+ self.options.data = parsed;//cache data forever - E.F.
+ self.cacheWrite(filter, parsed);
+ }
+ if (self._element){
+ self._element.removeClass(self.options.loadingClass);
+ }
+ callback(parsed);
+ };
+ $.ajax({
+ url: this.makeUrl(filter),
+ success: ajaxCallback,
+ error: function() {
+ ajaxCallback(false);
+ }
+ });
+ }
+};
+
+AutoCompleter.prototype.setOption = function(name, value){
+ this.options[name] = value;
+};
+
+AutoCompleter.prototype.setExtraParam = function(name, value) {
+ var index = $.trim(String(name));
+ if (index) {
+ if (!this.options.extraParams) {
+ this.options.extraParams = {};
+ }
+ if (this.options.extraParams[index] !== value) {
+ this.options.extraParams[index] = value;
+ this.cacheFlush();
+ }
+ }
+};
+
+AutoCompleter.prototype.makeUrl = function(param) {
+ var self = this;
+ var url = this.options.url;
+ var params = $.extend({}, this.options.extraParams);
+ // If options.queryParamName === false, append query to url
+ // instead of using a GET parameter
+ if (this.options.queryParamName === false) {
+ url += encodeURIComponent(param);
+ } else {
+ params[this.options.queryParamName] = param;
+ }
+
+ if (this.options.limitParamName && this.options.maxItemsToShow) {
+ params[this.options.limitParamName] = this.options.maxItemsToShow;
+ }
+
+ var urlAppend = [];
+ $.each(params, function(index, value) {
+ urlAppend.push(self.makeUrlParam(index, value));
+ });
+ if (urlAppend.length) {
+ url += url.indexOf('?') == -1 ? '?' : '&';
+ url += urlAppend.join('&');
+ }
+ return url;
+};
+
+AutoCompleter.prototype.makeUrlParam = function(name, value) {
+ return String(name) + '=' + encodeURIComponent(value);
+};
+
+/**
+ * Sanitize CR and LF, then split into lines
+ */
+AutoCompleter.prototype.splitText = function(text) {
+ return String(text).replace(/(\r\n|\r|\n)/g, '\n').split(this.options.lineSeparator);
+};
+
+AutoCompleter.prototype.parseRemoteData = function(remoteData) {
+ var value, lines, i, j, data;
+ var results = [];
+ var lines = this.splitText(remoteData);
+ for (i = 0; i < lines.length; i++) {
+ var line = lines[i].split(this.options.cellSeparator);
+ data = [];
+ for (j = 0; j < line.length; j++) {
+ data.push(unescape(line[j]));
+ }
+ value = data.shift();
+ results.push({ value: unescape(value), data: data });
+ }
+ return results;
+};
+
+AutoCompleter.prototype.filterAndShowResults = function(results, filter) {
+ this.showResults(this.filterResults(results, filter), filter);
+};
+
+AutoCompleter.prototype.filterResults = function(results, filter) {
+
+ var filtered = [];
+ var value, data, i, result, type, include;
+ var regex, pattern, testValue;
+
+ for (i = 0; i < results.length; i++) {
+ result = results[i];
+ type = typeof result;
+ if (type === 'string') {
+ value = result;
+ data = {};
+ } else if ($.isArray(result)) {
+ value = result[0];
+ data = result.slice(1);
+ } else if (type === 'object') {
+ value = result.value;
+ data = result.data;
+ }
+ value = String(value);
+ if (value > '') {
+ if (typeof data !== 'object') {
+ data = {};
+ }
+ if (this.options.filterResults) {
+ pattern = String(filter);
+ testValue = String(value);
+ if (!this.options.matchCase) {
+ pattern = pattern.toLowerCase();
+ testValue = testValue.toLowerCase();
+ }
+ include = testValue.indexOf(pattern);
+ if (this.options.matchInside) {
+ include = include > -1;
+ } else {
+ include = include === 0;
+ }
+ } else {
+ include = true;
+ }
+ if (include) {
+ filtered.push({ value: value, data: data });
+ }
+ }
+ }
+
+ if (this.options.sortResults) {
+ filtered = this.sortResults(filtered, filter);
+ }
+
+ if (this.options.maxItemsToShow > 0 && this.options.maxItemsToShow < filtered.length) {
+ filtered.length = this.options.maxItemsToShow;
+ }
+
+ return filtered;
+
+};
+
+AutoCompleter.prototype.sortResults = function(results, filter) {
+ var self = this;
+ var sortFunction = this.options.sortFunction;
+ if (!$.isFunction(sortFunction)) {
+ sortFunction = function(a, b, f) {
+ return self.sortValueAlpha(a, b, f);
+ };
+ }
+ results.sort(function(a, b) {
+ return sortFunction(a, b, filter);
+ });
+ return results;
+};
+
+AutoCompleter.prototype.sortValueAlpha = function(a, b, filter) {
+ a = String(a.value);
+ b = String(b.value);
+ if (!this.options.matchCase) {
+ a = a.toLowerCase();
+ b = b.toLowerCase();
+ }
+ if (a > b) {
+ return 1;
+ }
+ if (a < b) {
+ return -1;
+ }
+ return 0;
+};
+
+AutoCompleter.prototype.showResults = function(results, filter) {
+ var self = this;
+ var $ul = $('<ul></ul>');
+ var i, result, $li, extraWidth, first = false, $first = false;
+ var numResults = results.length;
+ for (i = 0; i < numResults; i++) {
+ result = results[i];
+ $li = $('<li>' + this.showResult(result.value, result.data) + '</li>');
+ $li.data('value', result.value);
+ $li.data('data', result.data);
+ $li.click(function() {
+ var $this = $(this);
+ self.selectItem($this);
+ }).mousedown(function() {
+ self.finishOnBlur_ = false;
+ }).mouseup(function() {
+ self.finishOnBlur_ = true;
+ });
+ $ul.append($li);
+ if (first === false) {
+ first = String(result.value);
+ $first = $li;
+ $li.addClass(this.options.firstItemClass);
+ }
+ if (i == numResults - 1) {
+ $li.addClass(this.options.lastItemClass);
+ }
+ }
+
+ // Alway recalculate position before showing since window size or
+ // input element location may have changed. This fixes #14
+ this.position();
+
+ this._results.html($ul).show();
+ extraWidth = this._results.outerWidth() - this._results.width();
+ this._results.width(this._element.outerWidth() - extraWidth);
+ $('li', this._results).hover(
+ function() { self.focusItem(this); },
+ function() { /* void */ }
+ );
+ if (this.autoFill(first, filter)) {
+ this.focusItem($first);
+ }
+};
+
+AutoCompleter.prototype.showResult = function(value, data) {
+ if ($.isFunction(this.options.showResult)) {
+ return this.options.showResult(value, data);
+ } else {
+ return value;
+ }
+};
+
+AutoCompleter.prototype.autoFill = function(value, filter) {
+ var lcValue, lcFilter, valueLength, filterLength;
+ if (this.options.autoFill && this.lastKeyPressed_ != 8) {
+ lcValue = String(value).toLowerCase();
+ lcFilter = String(filter).toLowerCase();
+ valueLength = value.length;
+ filterLength = filter.length;
+ if (lcValue.substr(0, filterLength) === lcFilter) {
+ this._element.val(value);
+ this.selectRange(filterLength, valueLength);
+ return true;
+ }
+ }
+ return false;
+};
+
+AutoCompleter.prototype.focusNext = function() {
+ this.focusMove(+1);
+};
+
+AutoCompleter.prototype.focusPrev = function() {
+ this.focusMove(-1);
+};
+
+AutoCompleter.prototype.focusMove = function(modifier) {
+ var i, $items = $('li', this._results);
+ modifier = parseInt(modifier, 10);
+ for (var i = 0; i < $items.length; i++) {
+ if ($($items[i]).hasClass(this.selectClass_)) {
+ this.focusItem(i + modifier);
+ return;
+ }
+ }
+ this.focusItem(0);
+};
+
+AutoCompleter.prototype.focusItem = function(item) {
+ var $item, $items = $('li', this._results);
+ if ($items.length) {
+ $items.removeClass(this.selectClass_).removeClass(this.options.selectClass);
+ if (typeof item === 'number') {
+ item = parseInt(item, 10);
+ if (item < 0) {
+ item = 0;
+ } else if (item >= $items.length) {
+ item = $items.length - 1;
+ }
+ $item = $($items[item]);
+ } else {
+ $item = $(item);
+ }
+ if ($item) {
+ $item.addClass(this.selectClass_).addClass(this.options.selectClass);
+ }
+ }
+};
+
+AutoCompleter.prototype.selectCurrent = function() {
+ var $item = $('li.' + this.selectClass_, this._results);
+ if ($item.length == 1) {
+ this.selectItem($item);
+ } else {
+ this.finish();
+ }
+};
+
+AutoCompleter.prototype.selectItem = function($li) {
+ var value = $li.data('value');
+ var data = $li.data('data');
+ var displayValue = this.displayValue(value, data);
+ this.lastProcessedValue_ = displayValue;
+ this.lastSelectedValue_ = displayValue;
+
+ this.setValue(displayValue);
+
+ this.setCaret(displayValue.length);
+ this.callHook('onItemSelect', { value: value, data: data });
+ this.finish();
+};
+
+/**
+ * @return {boolean} true if the symbol matches something that is
+ * considered content and false otherwise
+ * @param {string} symbol - a single char string
+ */
+AutoCompleter.prototype.isContentChar = function(symbol){
+ if (symbol.match(this.options['stopCharRegex'])){
+ return false;
+ } else if (symbol === this.options['multipleSeparator']){
+ return false;
+ } else {
+ return true;
+ }
+};
+
+/**
+ * takes value from the input box
+ * and saves _selection_start and _selection_end coordinates
+ * respects settings autocompleteMultiple and
+ * multipleSeparator
+ * @return {string} the current word in the
+ * autocompletable word
+ */
+AutoCompleter.prototype.getValue = function(){
+ var sel = this._element.getSelection();
+ var text = this._element.val();
+ var pos = sel.start;//estimated start
+ //find real start
+ var start = pos;
+ for (cpos = pos; cpos >= 0; cpos = cpos - 1){
+ if (cpos === text.length){
+ continue;
+ }
+ var symbol = text.charAt(cpos);
+ if (!this.isContentChar(symbol)){
+ break;
+ }
+ start = cpos;
+ }
+ //find real end
+ var end = pos;
+ for (cpos = pos; cpos < text.length; cpos = cpos + 1){
+ if (cpos === 0){
+ continue;
+ }
+ var symbol = text.charAt(cpos);
+ if (!this.isContentChar(symbol)){
+ break;
+ }
+ end = cpos;
+ }
+ this._selection_start = start;
+ this._selection_end = end;
+ return text.substring(start, end);
+}
+
+/**
+ * sets value of the input box
+ * by replacing the previous selection
+ * with the value from the autocompleter
+ */
+AutoCompleter.prototype.setValue = function(val){
+ var prefix = this._element.val().substring(0, this._selection_start);
+ var postfix = this._element.val().substring(this._selection_end + 1);
+ this._element.val(prefix + val + postfix);
+};
+
+AutoCompleter.prototype.displayValue = function(value, data) {
+ if ($.isFunction(this.options.displayValue)) {
+ return this.options.displayValue(value, data);
+ } else {
+ return value;
+ }
+};
+
+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(start, end) {
+ var input = this._element.get(0);
+ if (input.setSelectionRange) {
+ input.focus();
+ input.setSelectionRange(start, end);
+ } else if (this.createTextRange) {
+ var range = this.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', end);
+ range.moveStart('character', start);
+ range.select();
+ }
+};
+
+AutoCompleter.prototype.setCaret = function(pos) {
+ this.selectRange(pos, pos);
+};
+
(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);
/**
diff --git a/askbot/skins/common/media/js/wmd/wmd.js b/askbot/skins/common/media/js/wmd/wmd.js
index 60d6d868..5aeacd98 100644
--- a/askbot/skins/common/media/js/wmd/wmd.js
+++ b/askbot/skins/common/media/js/wmd/wmd.js
@@ -3,10 +3,10 @@
Attacklab.wmdBase = function(){
// A few handy aliases for readability.
- var wmd = top.Attacklab;
- var doc = top.document;
- var re = top.RegExp;
- var nav = top.navigator;
+ var wmd = self.Attacklab;
+ var doc = self.document;
+ var re = self.RegExp;
+ var nav = self.navigator;
// Some namespaces.
wmd.Util = {};
@@ -21,6 +21,7 @@ Attacklab.wmdBase = function(){
// Used to work around some browser bugs where we can't use feature testing.
+ global.isChrome = /chrome/.test(nav.userAgent.toLowerCase());
global.isIE = /msie/.test(nav.userAgent.toLowerCase());
global.isIE_5or6 = /msie 6/.test(nav.userAgent.toLowerCase()) || /msie 5/.test(nav.userAgent.toLowerCase());
global.isIE_7plus = global.isIE && !global.isIE_5or6;
@@ -1611,6 +1612,10 @@ util.prompt = function(text, defaultInputText, makeLinkMarkdown, dialogType){
var regexText;
var replacementText;
+
+ if (global.isChrome) {//Chrome bug workaround
+ 'X'.match(/()./);
+ }
this.selection = this.selection.replace(/(^\n*)/, "");
this.startTag = this.startTag + re.$1;
diff --git a/askbot/skins/common/templates/authopenid/complete.html b/askbot/skins/common/templates/authopenid/complete.html
index a84aa646..c9afedee 100644
--- a/askbot/skins/common/templates/authopenid/complete.html
+++ b/askbot/skins/common/templates/authopenid/complete.html
@@ -14,7 +14,6 @@ parameters:
* username (same as screen name or username in the models, and nickname in openid sreg)
* openid_register_form
* openid_verify_form - not clear what this form is supposed to do, not used for legacy
-* email_feeds_form forum.forms.SimpleEmailSubscribeForm
* openid_username_exists
#}
{% block head %}{% endblock %}
@@ -57,13 +56,6 @@ anyone, must be valid</i>)
{% endif %}
{{ openid_register_form.email }}
</div>
- <p>{% trans %}<strong>Receive forum updates by email</strong>{% endtrans %}</p>
- <div class='simple-subscribe-options'>
- {{email_feeds_form.subscribe}}
- {% if email_feeds_form.errors %}
- <p class="error">{% trans %}please select one of the options above{% endtrans %}</p>
- {% endif %}
- </div>
<div class="submit-row"><input type="submit" class="submit" name="bnewaccount" value="{% trans %}Signup{% endtrans %}"/></div>
</form>
</div>
diff --git a/askbot/skins/common/templates/authopenid/signin.html b/askbot/skins/common/templates/authopenid/signin.html
index 2cce65b1..c5a5c47f 100644
--- a/askbot/skins/common/templates/authopenid/signin.html
+++ b/askbot/skins/common/templates/authopenid/signin.html
@@ -24,25 +24,25 @@
{% endtrans %}
</div>
{% endif %}
+ {% if not (view_subtype == 'default' and have_buttons) %}
<p id='login-intro'>
- {% if view_subtype == 'default' and have_buttons %}
- {% trans %}Choose your favorite service below to sign in using secure OpenID or similar technology. Your external service password always stays confidential and you don't have to rememeber or create another one.{% endtrans %}
- {% elif view_subtype == 'add_openid' and have_buttons %}
- {% if existing_login_methods %}
- {% trans %}It's a good idea to make sure that your existing login methods still work, or add a new one. Please click any of the icons below to check/change or add new login methods.{% endtrans %}
- {% else %}
- {% trans %}Please add a more permanent login method by clicking one of the icons below, to avoid logging in via email each time.{% endtrans %}
- {% endif %}
- {% elif view_subtype == 'change_openid' and have_buttons %}
- {% if existing_login_methods %}
- {% trans %}Click on one of the icons below to add a new login method or re-validate an existing one.{% endtrans %}
- {% else %}
- {% trans %}You don't have a method to log in right now, please add one or more by clicking any of the icons below.{% endtrans %}
+ {% if view_subtype == 'add_openid' and have_buttons %}
+ {% if existing_login_methods %}
+ {% trans %}It's a good idea to make sure that your existing login methods still work, or add a new one. Please click any of the icons below to check/change or add new login methods.{% endtrans %}
+ {% else %}
+ {% trans %}Please add a more permanent login method by clicking one of the icons below, to avoid logging in via email each time.{% endtrans %}
+ {% endif %}
+ {% elif view_subtype == 'change_openid' and have_buttons %}
+ {% if existing_login_methods %}
+ {% trans %}Click on one of the icons below to add a new login method or re-validate an existing one.{% endtrans %}
+ {% else %}
+ {% trans %}You don't have a method to log in right now, please add one or more by clicking any of the icons below.{% endtrans %}
+ {% endif %}
+ {% elif view_subtype == 'email_sent' %}
+ {% trans %}Please check your email and visit the enclosed link to re-connect to your account{% endtrans %}
{% endif %}
- {% elif view_subtype == 'email_sent' %}
- {% trans %}Please check your email and visit the enclosed link to re-connect to your account{% endtrans %}
- {% endif %}
</p>
+ {% endif %}
{% if openid_error_message %}
<p class="warning">{{ openid_error_message }}</p>
{% endif %}
diff --git a/askbot/skins/common/templates/authopenid/signup_with_password.html b/askbot/skins/common/templates/authopenid/signup_with_password.html
index e65cd518..e5a8f633 100644
--- a/askbot/skins/common/templates/authopenid/signup_with_password.html
+++ b/askbot/skins/common/templates/authopenid/signup_with_password.html
@@ -37,15 +37,6 @@ your login details with anyone and having to remember yet another password.{% en
<li><label for="password1_id">{{form.password1.label}}</label>{{form.password1}}{{form.password1.errors}}</li>
<li><label for="password2_id">{{form.password2.label}}</label>{{form.password2}}{{form.password2.errors}}</li>
</ul>
- <p style="margin-top: 10px">
- {% trans %}<strong>Receive periodic updates by email</strong>{% endtrans %}
- </p>
- <div class='simple-subscribe-options'>
- {{email_feeds_form.subscribe}}
- {% if email_feeds_form.errors %}
- <p class="error">{% trans %}please select one of the options above{% endtrans %}</p>
- {% endif %}
- </div>
{% if settings.USE_RECAPTCHA %}
<p class="signup_p">{% trans %}Please read and type in the two words below to help us prevent automated account creation.{% endtrans %}</p>
{{form.recaptcha}}
diff --git a/askbot/skins/common/templates/authopenid/verify_email.html b/askbot/skins/common/templates/authopenid/verify_email.html
new file mode 100644
index 00000000..613ca589
--- /dev/null
+++ b/askbot/skins/common/templates/authopenid/verify_email.html
@@ -0,0 +1,14 @@
+{% extends "one_column_body.html" %}
+{% block title %}{% spaceless %}{% trans %}Confirm email address{% endtrans %}{% endspaceless %}{% endblock %}
+{% block content %}
+ <h1 class="section-title">{% trans %}Confirm email address{% endtrans %}</h1>
+ <label for="validation_code">
+ {% trans %}Validation email sent. Please find it and follow the enclosed link.<br/>
+ If the link doesn't work - enter the code below:{% endtrans %}
+ </label>
+ <form method="post">{% csrf_token %}
+ <input id="validation-code" type="text" name="validation_code" />
+ <input type="submit" class="submit" value="{% trans %}Confirm email{% endtrans %}" />
+ </form>
+{% endblock %}
+<!-- end changeemail.html -->
diff --git a/askbot/skins/common/templates/question/answer_controls.html b/askbot/skins/common/templates/question/answer_controls.html
index 50f40144..4efc7247 100644
--- a/askbot/skins/common/templates/question/answer_controls.html
+++ b/askbot/skins/common/templates/question/answer_controls.html
@@ -8,6 +8,22 @@
{% trans %}link{% endtrans %}
</a>
</span>
+<span
+ id="post-{{answer.id}}-publish"
+ class="action-link"
+>
+ {% if answer.id in published_answer_ids %}
+ <a
+ class="answer-unpublish"
+ data-answer-id="{{ answer.id }}"
+ >{% trans %}unpublish{% endtrans %}</a>
+ {% else %}
+ <a
+ class="answer-publish"
+ data-answer-id="{{ answer.id}}"
+ >{% trans %}publish{% endtrans %}</a>
+ {% endif %}
+</span>
<span id='post-{{answer.id}}-delete' class="action-link delete-post">
<a class="question-delete"
>{% if answer.deleted %}{% trans %}undelete{% endtrans %}{% else %}{% trans %}delete{% endtrans %}{% endif %}</a>
@@ -26,7 +42,6 @@
title="{% trans %}report as offensive (i.e containing spam, advertising, malicious text, etc.){% endtrans %}"
>
<a class="question-flag">{% trans %}flag offensive{% endtrans %} ({{ answer.offensive_flag_count }})</a>
- </a>
</span>
{% else %}
<span
@@ -40,6 +55,15 @@
<span id='post-{{answer.id}}-edit' class="action-link">
<a class="question-edit" href="{% url edit_answer answer.id %}">{% trans %}edit{% endtrans %}</a>
</span>
+<span id='post-{{answer.id}}-convert' class="action-link">
+ <form class="answer-convert" action="{% url answer_to_comment %}" method="POST">
+ {% csrf_token %}
+ <input type="hidden" name="answer_id" id="id_answer_id" value="{{answer.id}}"/>
+ <input type="submit" name="" value="{% trans %}convert to comment{% endtrans %}"/>
+ </form>
+</span>
<script type="text/javascript">
- askbot['functions']['renderPostControls']('{{answer.id}}');
+ askbot['functions']['hideConvertAnswerLinks']('{{ answer.id }}');
+ askbot['functions']['hidePublishAnswerLink']('{{ answer.id }}');
+ askbot['functions']['renderPostControls']('{{ answer.id }}');
</script>
diff --git a/askbot/skins/common/templates/widgets/edit_post.html b/askbot/skins/common/templates/widgets/edit_post.html
index 89d7f6f3..b9bfa1e3 100644
--- a/askbot/skins/common/templates/widgets/edit_post.html
+++ b/askbot/skins/common/templates/widgets/edit_post.html
@@ -36,7 +36,7 @@
{% include "widgets/tag_editor.html" %}
{% else %}
{% if tags_are_required %}
- <label for=id_tags">
+ <label for="id_tags">
{% if mandatory_tags %}
<strong>{% trans %}tags{% endtrans %}</strong>
{% trans %}, one of these is required{% endtrans %}
diff --git a/askbot/skins/default/media/bootstrap/css/bootstrap.css b/askbot/skins/default/media/bootstrap/css/bootstrap.css
index b5f04009..e6190005 100644
--- a/askbot/skins/default/media/bootstrap/css/bootstrap.css
+++ b/askbot/skins/default/media/bootstrap/css/bootstrap.css
@@ -919,7 +919,7 @@ input[type="button"],
input[type="reset"],
input[type="submit"] {
width: auto;
- height: auto;
+ /*height: auto;*/
}
select,
input[type="file"] {
diff --git a/askbot/skins/default/media/images/publish.png b/askbot/skins/default/media/images/publish.png
new file mode 100644
index 00000000..038a87d2
--- /dev/null
+++ b/askbot/skins/default/media/images/publish.png
Binary files differ
diff --git a/askbot/skins/default/media/images/sprites.png b/askbot/skins/default/media/images/sprites.png
index 8c513508..c4e9029c 100644
--- a/askbot/skins/default/media/images/sprites.png
+++ b/askbot/skins/default/media/images/sprites.png
Binary files differ
diff --git a/askbot/skins/default/media/images/sprites_source/sprites.svg b/askbot/skins/default/media/images/sprites_source/sprites.svg
index 585e578f..1c16c89c 100644
--- a/askbot/skins/default/media/images/sprites_source/sprites.svg
+++ b/askbot/skins/default/media/images/sprites_source/sprites.svg
@@ -14,9 +14,9 @@
height="207"
id="svg3448"
version="1.1"
- inkscape:version="0.48.1 r9760"
+ inkscape:version="0.48.3.1 r9886"
sodipodi:docname="sprites.svg"
- inkscape:export-filename="/home/bcorrales/personal/askbot/sprites.png"
+ inkscape:export-filename="/home/fitoria/code/askbot-devel/askbot/skins/default/media/images/sprites.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
@@ -221,16 +221,16 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="1"
- inkscape:cx="-14.220783"
- inkscape:cy="199.46611"
+ inkscape:zoom="1.8095563"
+ inkscape:cx="-20.257428"
+ inkscape:cy="137.9209"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
- inkscape:window-width="1316"
- inkscape:window-height="744"
- inkscape:window-x="50"
- inkscape:window-y="24"
+ inkscape:window-width="1366"
+ inkscape:window-height="723"
+ inkscape:window-x="-3"
+ inkscape:window-y="-3"
inkscape:window-maximized="1" />
<metadata
id="metadata3453">
@@ -240,7 +240,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
+ <dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -348,69 +348,81 @@
x="28.158876"
y="953.7583"
style="font-size:14.30124187px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#e90f0f;fill-opacity:1;font-family:Trebuchet MS;-inkscape-font-specification:Trebuchet MS">X</tspan></text>
- <path
- inkscape:export-ydpi="90"
- inkscape:export-xdpi="90"
- inkscape:export-filename="/home/bcorrales/personal/oxfam/arte/disenoindex.png"
- id="path5443"
- d="m 60.268935,856.20911 c -1.0424,-0.85947 -2.66478,-0.61037 -3.61221,0.54899 -0.95263,1.15795 -0.86987,2.79882 0.17873,3.65569 1.0444,0.86116 2.66667,0.61206 3.61631,-0.54959 0.94503,-1.16205 0.86746,-2.79822 -0.17854,-3.65559 z m 5.88263,18.6866 -12.99549,-10.63781 c -0.44703,-0.36346 -0.64755,-0.93014 -0.58638,-1.50242 -0.05,-0.11386 -0.087,-0.2505 -0.10985,-0.41064 l -1.33739,-8.83579 c -0.14424,-0.94964 0.5206,-1.76262 1.48163,-1.8085 l 8.91926,-0.44173 c 0.38495,-0.019 1.49952,0.35486 1.78501,0.58457 l 12.99449,10.6375 c 0.74232,0.60366 0.7989,1.77001 0.12665,2.5924 l -7.71322,9.43427 c -0.66824,0.82178 -1.8207,0.99492 -2.56061,0.38925 z"
- clip-rule="evenodd"
- style="fill:#e7e8a8;fill-opacity:1;fill-rule:evenodd;stroke:none"
- inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- style="fill:#16160f;fill-opacity:1;fill-rule:evenodd"
- clip-rule="evenodd"
- d="m 60.685375,857.34507 c -0.92055,-0.75811 -2.28652,-0.61896 -3.04183,0.30448 -0.75951,0.92235 -0.62036,2.28942 0.30498,3.04553 0.92224,0.7595 2.28822,0.62046 3.04513,-0.30508 0.75321,-0.92595 0.61866,-2.28902 -0.30479,-3.04533 z m 5.77547,15.77522 -11.47288,-9.38529 c -0.39445,-0.32067 -0.58837,-0.79999 -0.56038,-1.27301 -0.047,-0.097 -0.084,-0.21191 -0.11056,-0.34586 l -1.50572,-7.39855 c -0.16224,-0.79519 0.36786,-1.44284 1.18105,-1.44084 l 7.54948,0.01 c 0.32578,4e-4 1.28701,0.35796 1.53911,0.56048 l 11.47158,9.38508 c 0.65525,0.53279 0.75211,1.50442 0.21612,2.15966 l -6.14833,7.51602 c -0.5325,0.65474 -1.50312,0.75031 -2.15627,0.21591 z"
- id="path5445"
- inkscape:export-filename="/home/bcorrales/personal/oxfam/arte/disenoindex.png"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90"
- inkscape:transform-center-x="0.5180824"
- inkscape:transform-center-y="-0.28622416" />
- <path
- inkscape:connector-curvature="0"
- id="path5457"
- d="m 143.74707,850.88647 c -5.0386,0.15973 -7.45306,6.82507 -4.44845,10.23363 -5.35784,2.62945 -3.73419,8.64761 -3.90826,13.70497 6.23105,0 12.46221,0 18.69326,0 -0.0342,-5.17704 1.38659,-11.61813 -4.44823,-13.9621 3.5491,-4.27537 -0.52838,-10.73631 -5.88832,-9.9765 z"
- style="fill:#e7e8a8;fill-opacity:1;stroke:none" />
- <path
- sodipodi:type="arc"
- style="fill:#16160f;fill-opacity:1;stroke:none"
- id="path5461"
- sodipodi:cx="-1766"
- sodipodi:cy="1210.3622"
- sodipodi:rx="20.875"
- sodipodi:ry="19.875"
- d="m -1746.6683,1217.8622 c -4.3506,10.1651 -16.5325,15.0477 -27.209,10.9056 -10.6766,-4.1422 -15.8049,-15.7405 -11.4544,-25.9056 4.3506,-10.1651 16.5325,-15.0477 27.209,-10.9056 10.6563,4.1342 15.789,15.6991 11.4755,25.8559"
- sodipodi:start="0.38694167"
- sodipodi:end="6.6674319"
- sodipodi:open="true"
- transform="matrix(0.23246322,0,0,0.23246322,554.98367,575.74893)" />
- <path
- style="fill:#16160f;fill-opacity:1;stroke:none"
- d="m 142.46627,862.02883 c -2.77296,0 -5.03938,2.03973 -5.40307,4.70966 l -0.021,0 0,0.11384 c -0.0243,0.20805 -0.0408,0.42712 -0.0408,0.64178 0,0.21103 0.0177,0.41632 0.0408,0.62106 l 0,5.0718 15.40196,0 0,-5.0718 c 0.0232,-0.20474 0.0408,-0.41003 0.0408,-0.62106 0,-0.21466 -0.0177,-0.43373 -0.0408,-0.64178 l 0,-0.11384 -0.021,0 c -0.3638,-2.66993 -2.63021,-4.70966 -5.40306,-4.70966 l -1.03504,0 -1.35592,2.81554 -1.36629,-2.81554 -0.79699,0 z"
- id="path5463"
- inkscape:connector-curvature="0" />
- <path
- style="opacity:0.95734594;fill:#e7e8a8;fill-opacity:1;stroke:none"
- d="m 230.09729,852.03013 c -1.6542,0.0555 -2.75231,2.08638 -1.97699,3.52883 -0.63135,1.02386 -1.18707,3.28768 -2.84533,2.51282 -1.58254,0.6738 -2.41713,-1.60371 -3.98172,-0.91457 -2.15656,0.50838 -2.50044,3.95528 -0.28646,4.60984 0.66658,0.11836 1.38854,0.19214 1.38577,1.01628 1.32012,1.04282 0.20536,3.35241 -1.27491,3.51052 -2.03524,0.99474 -1.52604,4.44609 0.79456,4.64674 1.48119,0.14147 2.53871,-1.5326 4.09258,-0.89608 1.17551,0.24938 1.76747,1.60242 2.05078,2.54048 -1.0087,1.8897 1.18605,4.16804 3.10405,3.20566 1.39067,-0.4601 1.85188,-2.07121 1.37652,-3.31653 0.41369,-0.83912 1.05271,-1.65965 1.6075,-2.34648 1.24643,-0.35941 2.55425,-0.14896 3.50119,0.71142 2.08378,0.88286 4.38154,-1.75378 2.95628,-3.63073 -0.53953,-1.15942 -2.19299,-0.66667 -2.50358,-1.94916 -0.82635,-1.06372 -0.34544,-2.40575 0.47111,-3.2519 0.51485,-0.51216 1.80066,-0.20231 2.15249,-1.1363 1.70737,-1.91245 -1.15266,-5.07734 -3.21491,-3.54741 -0.69552,1.09312 -1.98439,0.81045 -3.03932,0.8406 -0.98808,-0.56654 -1.66816,-1.89082 -1.84763,-2.90081 0.49774,-1.68564 -0.70875,-3.40169 -2.52198,-3.23322 z"
- id="path5491"
- inkscape:connector-curvature="0" />
- <path
- id="path5404"
- d="m 230.30968,853.20332 c -0.69043,0 -1.25641,0.56589 -1.25641,1.25632 0,0.41517 0.20601,0.77958 0.51734,1.00695 l -2.22628,3.81547 -4.22185,-0.009 c 0,0 0,-0.009 0,-0.009 -0.11993,-0.56552 -0.62747,-0.98854 -1.22877,-0.98854 -0.69034,0 -1.24707,0.55663 -1.24707,1.24716 0,0.69044 0.55673,1.24708 1.24707,1.24708 0.21813,0 0.4221,-0.0582 0.60056,-0.15692 l 2.01389,3.51967 -1.85688,3.19642 c -0.13232,-0.0472 -0.27629,-0.0841 -0.42497,-0.0841 -0.69044,0 -1.24717,0.55674 -1.24717,1.24727 0,0.69052 0.55673,1.25641 1.24717,1.25641 0.54915,0 1.01452,-0.35784 1.18244,-0.84994 l 3.88926,-0.0185 2.19872,3.72291 c -0.28692,0.22904 -0.48036,0.57421 -0.48036,0.96996 0,0.69053 0.55664,1.24726 1.24717,1.24726 0.69044,0 1.25641,-0.55673 1.25641,-1.24726 0,-0.37153 -0.16764,-0.70403 -0.42497,-0.93297 l 2.24487,-3.7599 4.02786,-0.009 c 0.13796,0.54037 0.62682,0.94231 1.21018,0.94231 0.69053,0 1.24717,-0.55673 1.24717,-1.24716 0,-0.69053 -0.55664,-1.25643 -1.24717,-1.25643 -0.18003,0 -0.3534,0.0425 -0.5081,0.11087 l -1.95849,-3.4089 2.05087,-3.53826 c 0.18262,0.10449 0.38447,0.16634 0.60971,0.16634 0.69053,0 1.25641,-0.55663 1.25641,-1.24707 0,-0.69062 -0.56588,-1.24726 -1.25641,-1.24726 -0.63921,0 -1.15415,0.48026 -1.22867,1.09941 l -4.13864,0.0185 -2.32808,-3.83387 c 0.28794,-0.22904 0.48045,-0.57347 0.48045,-0.96996 0,-0.69053 -0.55673,-1.25641 -1.24726,-1.25641 z"
- style="opacity:0.95734594;fill:#16160f;fill-opacity:1;stroke:none"
- inkscape:connector-curvature="0" />
- <path
- transform="matrix(0.05048908,0,0,0.04995915,374.93399,727.37564)"
- d="m -2780.5,2735.8623 c 0,45.5635 -37.3842,82.5 -83.5,82.5 -46.1158,0 -83.5,-36.9365 -83.5,-82.5 0,-45.5635 37.3842,-82.5 83.5,-82.5 46.1158,0 83.5,36.9365 83.5,82.5 z"
- sodipodi:ry="82.5"
- sodipodi:rx="83.5"
- sodipodi:cy="2735.8623"
- sodipodi:cx="-2864"
- id="path5754"
- style="opacity:0.95734594;fill:#e7e8a8;fill-opacity:1;stroke:none"
- sodipodi:type="arc" />
+ <g
+ id="g3045"
+ transform="translate(-46.460961,89.75573)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#e7e8a8;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ clip-rule="evenodd"
+ d="m 60.268935,856.20911 c -1.0424,-0.85947 -2.66478,-0.61037 -3.61221,0.54899 -0.95263,1.15795 -0.86987,2.79882 0.17873,3.65569 1.0444,0.86116 2.66667,0.61206 3.61631,-0.54959 0.94503,-1.16205 0.86746,-2.79822 -0.17854,-3.65559 z m 5.88263,18.6866 -12.99549,-10.63781 c -0.44703,-0.36346 -0.64755,-0.93014 -0.58638,-1.50242 -0.05,-0.11386 -0.087,-0.2505 -0.10985,-0.41064 l -1.33739,-8.83579 c -0.14424,-0.94964 0.5206,-1.76262 1.48163,-1.8085 l 8.91926,-0.44173 c 0.38495,-0.019 1.49952,0.35486 1.78501,0.58457 l 12.99449,10.6375 c 0.74232,0.60366 0.7989,1.77001 0.12665,2.5924 l -7.71322,9.43427 c -0.66824,0.82178 -1.8207,0.99492 -2.56061,0.38925 z"
+ id="path5443"
+ inkscape:export-filename="/home/bcorrales/personal/oxfam/arte/disenoindex.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <path
+ inkscape:transform-center-y="-0.28622416"
+ inkscape:transform-center-x="0.5180824"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ inkscape:export-filename="/home/bcorrales/personal/oxfam/arte/disenoindex.png"
+ id="path5445"
+ d="m 60.685375,857.34507 c -0.92055,-0.75811 -2.28652,-0.61896 -3.04183,0.30448 -0.75951,0.92235 -0.62036,2.28942 0.30498,3.04553 0.92224,0.7595 2.28822,0.62046 3.04513,-0.30508 0.75321,-0.92595 0.61866,-2.28902 -0.30479,-3.04533 z m 5.77547,15.77522 -11.47288,-9.38529 c -0.39445,-0.32067 -0.58837,-0.79999 -0.56038,-1.27301 -0.047,-0.097 -0.084,-0.21191 -0.11056,-0.34586 l -1.50572,-7.39855 c -0.16224,-0.79519 0.36786,-1.44284 1.18105,-1.44084 l 7.54948,0.01 c 0.32578,4e-4 1.28701,0.35796 1.53911,0.56048 l 11.47158,9.38508 c 0.65525,0.53279 0.75211,1.50442 0.21612,2.15966 l -6.14833,7.51602 c -0.5325,0.65474 -1.50312,0.75031 -2.15627,0.21591 z"
+ clip-rule="evenodd"
+ style="fill:#16160f;fill-opacity:1;fill-rule:evenodd"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g3049"
+ transform="translate(-130.64701,127.66187)">
+ <path
+ style="fill:#e7e8a8;fill-opacity:1;stroke:none"
+ d="m 143.74707,850.88647 c -5.0386,0.15973 -7.45306,6.82507 -4.44845,10.23363 -5.35784,2.62945 -3.73419,8.64761 -3.90826,13.70497 6.23105,0 12.46221,0 18.69326,0 -0.0342,-5.17704 1.38659,-11.61813 -4.44823,-13.9621 3.5491,-4.27537 -0.52838,-10.73631 -5.88832,-9.9765 z"
+ id="path5457"
+ inkscape:connector-curvature="0" />
+ <path
+ transform="matrix(0.23246322,0,0,0.23246322,554.98367,575.74893)"
+ sodipodi:open="true"
+ sodipodi:end="6.6674319"
+ sodipodi:start="0.38694167"
+ d="m -1746.6683,1217.8622 c -4.3506,10.1651 -16.5325,15.0477 -27.209,10.9056 -10.6766,-4.1422 -15.8049,-15.7405 -11.4544,-25.9056 4.3506,-10.1651 16.5325,-15.0477 27.209,-10.9056 10.6563,4.1342 15.789,15.6991 11.4755,25.8559"
+ sodipodi:ry="19.875"
+ sodipodi:rx="20.875"
+ sodipodi:cy="1210.3622"
+ sodipodi:cx="-1766"
+ id="path5461"
+ style="fill:#16160f;fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5463"
+ d="m 142.46627,862.02883 c -2.77296,0 -5.03938,2.03973 -5.40307,4.70966 l -0.021,0 0,0.11384 c -0.0243,0.20805 -0.0408,0.42712 -0.0408,0.64178 0,0.21103 0.0177,0.41632 0.0408,0.62106 l 0,5.0718 15.40196,0 0,-5.0718 c 0.0232,-0.20474 0.0408,-0.41003 0.0408,-0.62106 0,-0.21466 -0.0177,-0.43373 -0.0408,-0.64178 l 0,-0.11384 -0.021,0 c -0.3638,-2.66993 -2.63021,-4.70966 -5.40306,-4.70966 l -1.03504,0 -1.35592,2.81554 -1.36629,-2.81554 -0.79699,0 z"
+ style="fill:#16160f;fill-opacity:1;stroke:none" />
+ </g>
+ <g
+ id="g3054"
+ transform="translate(-214.86075,163.94135)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5491"
+ d="m 230.09729,852.03013 c -1.6542,0.0555 -2.75231,2.08638 -1.97699,3.52883 -0.63135,1.02386 -1.18707,3.28768 -2.84533,2.51282 -1.58254,0.6738 -2.41713,-1.60371 -3.98172,-0.91457 -2.15656,0.50838 -2.50044,3.95528 -0.28646,4.60984 0.66658,0.11836 1.38854,0.19214 1.38577,1.01628 1.32012,1.04282 0.20536,3.35241 -1.27491,3.51052 -2.03524,0.99474 -1.52604,4.44609 0.79456,4.64674 1.48119,0.14147 2.53871,-1.5326 4.09258,-0.89608 1.17551,0.24938 1.76747,1.60242 2.05078,2.54048 -1.0087,1.8897 1.18605,4.16804 3.10405,3.20566 1.39067,-0.4601 1.85188,-2.07121 1.37652,-3.31653 0.41369,-0.83912 1.05271,-1.65965 1.6075,-2.34648 1.24643,-0.35941 2.55425,-0.14896 3.50119,0.71142 2.08378,0.88286 4.38154,-1.75378 2.95628,-3.63073 -0.53953,-1.15942 -2.19299,-0.66667 -2.50358,-1.94916 -0.82635,-1.06372 -0.34544,-2.40575 0.47111,-3.2519 0.51485,-0.51216 1.80066,-0.20231 2.15249,-1.1363 1.70737,-1.91245 -1.15266,-5.07734 -3.21491,-3.54741 -0.69552,1.09312 -1.98439,0.81045 -3.03932,0.8406 -0.98808,-0.56654 -1.66816,-1.89082 -1.84763,-2.90081 0.49774,-1.68564 -0.70875,-3.40169 -2.52198,-3.23322 z"
+ style="opacity:0.95734594;fill:#e7e8a8;fill-opacity:1;stroke:none" />
+ <path
+ inkscape:connector-curvature="0"
+ style="opacity:0.95734594;fill:#16160f;fill-opacity:1;stroke:none"
+ d="m 230.30968,853.20332 c -0.69043,0 -1.25641,0.56589 -1.25641,1.25632 0,0.41517 0.20601,0.77958 0.51734,1.00695 l -2.22628,3.81547 -4.22185,-0.009 c 0,0 0,-0.009 0,-0.009 -0.11993,-0.56552 -0.62747,-0.98854 -1.22877,-0.98854 -0.69034,0 -1.24707,0.55663 -1.24707,1.24716 0,0.69044 0.55673,1.24708 1.24707,1.24708 0.21813,0 0.4221,-0.0582 0.60056,-0.15692 l 2.01389,3.51967 -1.85688,3.19642 c -0.13232,-0.0472 -0.27629,-0.0841 -0.42497,-0.0841 -0.69044,0 -1.24717,0.55674 -1.24717,1.24727 0,0.69052 0.55673,1.25641 1.24717,1.25641 0.54915,0 1.01452,-0.35784 1.18244,-0.84994 l 3.88926,-0.0185 2.19872,3.72291 c -0.28692,0.22904 -0.48036,0.57421 -0.48036,0.96996 0,0.69053 0.55664,1.24726 1.24717,1.24726 0.69044,0 1.25641,-0.55673 1.25641,-1.24726 0,-0.37153 -0.16764,-0.70403 -0.42497,-0.93297 l 2.24487,-3.7599 4.02786,-0.009 c 0.13796,0.54037 0.62682,0.94231 1.21018,0.94231 0.69053,0 1.24717,-0.55673 1.24717,-1.24716 0,-0.69053 -0.55664,-1.25643 -1.24717,-1.25643 -0.18003,0 -0.3534,0.0425 -0.5081,0.11087 l -1.95849,-3.4089 2.05087,-3.53826 c 0.18262,0.10449 0.38447,0.16634 0.60971,0.16634 0.69053,0 1.25641,-0.55663 1.25641,-1.24707 0,-0.69062 -0.56588,-1.24726 -1.25641,-1.24726 -0.63921,0 -1.15415,0.48026 -1.22867,1.09941 l -4.13864,0.0185 -2.32808,-3.83387 c 0.28794,-0.22904 0.48045,-0.57347 0.48045,-0.96996 0,-0.69053 -0.55673,-1.25641 -1.24726,-1.25641 z"
+ id="path5404" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.95734594;fill:#e7e8a8;fill-opacity:1;stroke:none"
+ id="path5754"
+ sodipodi:cx="-2864"
+ sodipodi:cy="2735.8623"
+ sodipodi:rx="83.5"
+ sodipodi:ry="82.5"
+ d="m -2780.5,2735.8623 c 0,45.5635 -37.3842,82.5 -83.5,82.5 -46.1158,0 -83.5,-36.9365 -83.5,-82.5 0,-45.5635 37.3842,-82.5 83.5,-82.5 46.1158,0 83.5,36.9365 83.5,82.5 z"
+ transform="matrix(0.05048908,0,0,0.04995915,374.93399,727.37564)" />
+ </g>
<path
style="fill:url(#radialGradient5712-8);fill-opacity:1"
d="m 9.7549531,922.45495 c -0.094,-0.046 -0.2146,-0.1465 -0.2675,-0.2235 -0.092,-0.1328 -0.097,-0.507 -0.1104,-7.2852 l -0.014,-7.1451 5.5932999,-4.8985 c 3.0763,-2.6941 5.6262,-4.9108 5.6664,-4.9262 0.044,-0.017 2.291499,1.9154 5.694699,4.8961 l 5.6214,4.9239 -0.014,7.1474 c -0.013,6.7953 -0.019,7.1546 -0.1108,7.2883 -0.2172,0.3162 -0.1184,0.3086 -4.0168,0.3086 l -3.5623,0 -6e-4,-3.0266 c -6e-4,-1.6648 -0.016,-3.1187 -0.035,-3.2313 -0.022,-0.1319 -0.098,-0.2677 -0.2151,-0.3832 l -0.1811,-0.1789 -3.152299,0 -3.1523,0 -0.181,0.1789 c -0.1168,0.1155 -0.1931,0.2513 -0.2152,0.3832 -0.019,0.1126 -0.034,1.5665 -0.035,3.2313 l -5e-4,3.0266 -3.5704,0 c -3.1432,0 -3.5909999,-0.012 -3.7417999,-0.085 z m -4.3178,-15.0177 c -0.4595,-0.5139 -0.8170004,-0.9502 -0.7943004,-0.9695 0.023,-0.019 3.6131004,-3.1609 7.9784003,-6.9811 4.3654,-3.82019 7.9797,-6.94589 8.0317,-6.94589 0.052,0 3.651199,3.11599 7.998399,6.92429 4.3473,3.8085 7.9377,6.949 7.9789,6.9791 0.059,0.043 -0.096,0.24591 -0.764,0.9929 -0.7293,0.8159 -0.8498,0.9293 -0.9245,0.8704 -0.047,-0.038 -3.2709,-2.8571 -7.1641,-6.26639 -3.8931,-3.40961 -7.098499,-6.1989 -7.123099,-6.1989 -0.025,0 -3.2475,2.8051 -7.1619,6.23339 -3.9143999,3.4285 -7.1400999,6.2475 -7.1684999,6.2648 -0.028,0.018 -0.4273,-0.3892 -0.887,-0.9031 z m 3.9274,-9.9518 0,-2.96499 2.0325999,0 2.0326,0 0,0.9707 c 0,1.5539 0.2495,1.16 -2.0161,3.1826 -1.0729,0.95779 -1.9728999,1.74949 -1.9998999,1.75909 -0.03,0.011 -0.048,-1.1677 -0.048,-2.9474 z"
@@ -501,7 +513,7 @@
sodipodi:cy="147.08873"
sodipodi:rx="11.136931"
sodipodi:ry="9.0156116"
- d="m 234.75944,147.08873 a 11.136931,9.0156116 0 1 1 -22.27386,0 11.136931,9.0156116 0 1 1 22.27386,0 z"
+ d="m 234.75944,147.08873 c 0,4.97918 -4.98617,9.01561 -11.13693,9.01561 -6.15075,0 -11.13693,-4.03643 -11.13693,-9.01561 0,-4.97918 4.98618,-9.01561 11.13693,-9.01561 6.15076,0 11.13693,4.03643 11.13693,9.01561 z"
transform="matrix(1.2054549,0,0,1.1341085,-99.371969,741.25595)" />
</g>
</svg>
diff --git a/askbot/skins/default/media/images/unpublish.png b/askbot/skins/default/media/images/unpublish.png
new file mode 100644
index 00000000..bfac25b1
--- /dev/null
+++ b/askbot/skins/default/media/images/unpublish.png
Binary files differ
diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css
index c8d92f60..3abbd362 100644
--- a/askbot/skins/default/media/style/style.css
+++ b/askbot/skins/default/media/style/style.css
@@ -29,7 +29,7 @@ body {
line-height: 150%;
margin: 0;
padding: 0;
- color: #000;
+ color: #666;
font-family: Arial;
}
div {
@@ -67,6 +67,12 @@ select {
font-family: Trebuchet MS, "segoe ui", Helvetica, Tahoma, Verdana, MingLiu, PMingLiu, Arial, sans-serif;
margin-left: 0px;
}
+input[type="text"].prompt,
+input[type="password"].prompt,
+input.tipped-input.blank {
+ font-style: italic;
+ color: #707070;
+}
textarea:focus,
input:focus {
outline: none;
@@ -194,12 +200,12 @@ body.user-messages {
padding: 0;
text-align: center;
background-color: #f5dd69;
- border-top: #fff 1px solid;
font-family: 'Open Sans Condensed', Arial, sans-serif;
}
-.notify p.notification {
+.notify .notification {
margin-top: 6px;
- margin-bottom: 6px;
+ /*margin-bottom: 6px;*/
+
font-size: 16px;
color: #424242;
}
@@ -280,7 +286,7 @@ body.user-messages {
#metaNav a {
color: #e2e2ae;
padding: 0px 0px 0px 35px;
- height: 25px;
+ height: 35px;
line-height: 25px;
margin: 5px 0px 0px 10px;
font-size: 18px;
@@ -307,16 +313,54 @@ body.user-messages {
text-decoration: underline;
}
#metaNav #navTags {
- background: -50px -5px url(../images/sprites.png) no-repeat;
-}
-#metaNav #navUsers {
- background: -125px -5px url(../images/sprites.png) no-repeat;
+ background: 0px -95px url(../images/sprites.png) no-repeat;
}
+#metaNav #navUsers,
#metaNav #navGroups {
- background: -125px -5px url(../images/sprites.png) no-repeat;
+ background: 3px -133px url(../images/sprites.png) no-repeat;
}
#metaNav #navBadges {
- background: -210px -5px url(../images/sprites.png) no-repeat;
+ background: 3px -170px url(../images/sprites.png) no-repeat;
+}
+#metaNav a.group-name {
+ padding: 0px;
+ float: center;
+ margin: 5px 0px 5px 10px;
+}
+#metaNav input.group-name {
+ border: none;
+ height: 25px;
+ font-size: 18px;
+ font-weight: 100;
+ text-decoration: none;
+ display: block;
+ margin: 0px 10px 0px 10px;
+ width: 140px;
+ font-family: 'Open Sans Condensed', Arial, sans-serif;
+ font-weight: 100;
+}
+#metaNav input.group-name:focus {
+ border: none;
+}
+#metaNav a.group-name:hover {
+ background-color: transparent;
+}
+#metaNav span.dropdown:hover ul.dropdown-menu {
+ display: block;
+}
+#metaNav div.dropdown-container:hover ul.dropdown-menu {
+ display: block;
+}
+#metaNav .dropdown {
+ float: left;
+}
+#metaNav .dropdown-menu {
+ border-top: none;
+ left: 7%;
+}
+#metaNav .dropdown-menu a {
+ color: #666;
+ height: 25px;
}
#header.with-logo #userToolsNav {
position: absolute;
@@ -327,9 +371,6 @@ body.user-messages {
float: left;
margin-top: 7px;
}
-#header.without-logo #metaNav {
- margin-bottom: 7px;
-}
#secondaryHeader {
/* Div containing Home button, scope navigation, search form and ask button, check blocks/secondary_header.html */
@@ -372,6 +413,22 @@ body.user-messages {
#secondaryHeader #scopeWrapper .ask-message {
font-size: 24px;
}
+.validate-email-page label {
+ color: #707070;
+ line-height: 1.35;
+ display: block;
+ margin: 10px 0;
+}
+.validate-email-page #validation-code {
+ padding-left: 5px;
+ border: #cce6ec 3px solid;
+ height: 25px;
+ font-size: 14px;
+ width: 200px;
+}
+.validate-email-page form {
+ margin-bottom: 30px;
+}
#searchBar {
/* Main search form , check widgets/search_bar.html */
@@ -527,6 +584,7 @@ body.anon #searchBar .searchInputCancelable {
margin-bottom: 4px;
color: #707070;
font-family: 'Open Sans Condensed', Arial, sans-serif;
+ font-size: 14px;
}
.box p.info-box-follow-up-links {
text-align: right;
@@ -557,6 +615,9 @@ body.anon #searchBar .searchInputCancelable {
.box .contributorback {
background: #eceeeb url(../images/contributorsback.png) no-repeat center left;
}
+.box form {
+ margin: 0px;
+}
.box label {
color: #707070;
font-size: 15px;
@@ -600,6 +661,7 @@ body.anon #searchBar .searchInputCancelable {
padding-left: 5px;
border: #c9c9b5 1px solid;
height: 25px;
+ font-size: 14px;
}
.box .inputs #ab-tag-search {
width: 138px;
@@ -762,13 +824,20 @@ body.anon #searchBar .searchInputCancelable {
}
.questions-related p {
line-height: 20px;
- padding: 4px 0px 4px 0px;
+ padding: 4px 0px 9px 0px;
font-size: 16px;
font-weight: normal;
border-bottom: #cccccc 1px solid;
}
+.questions-related p:first-child {
+ margin-top: -4px;
+}
+.questions-related p:last-child {
+ border: none;
+}
.questions-related a {
font-size: 13px;
+ line-height: 1.3;
}
/* tips and markdown help are widgets for ask template */
#tips li {
@@ -1239,11 +1308,68 @@ ul#related-tags li {
.tags a:hover {
color: #1A1A1A;
}
+.users-page th,
+.tags-page th,
+.groups-page th,
+.moderate-tags-page th {
+ padding-bottom: 5px;
+ font-weight: normal;
+}
.users-page h1,
.tags-page h1,
-.groups-page h1 {
+.groups-page h1,
+.moderate-tags-page h1 {
float: left;
}
+.moderate-tags-page button {
+ line-height: 18px;
+}
+.moderate-tags-page table {
+ border-spacing: 0;
+}
+.moderate-tags-page table.suggested-tags-table {
+ width: 100%;
+}
+.moderate-tags-page th {
+ font-style: italic;
+}
+.moderate-tags-page th,
+.moderate-tags-page tr {
+ vertical-align: top;
+ text-align: left;
+ padding-right: 20px;
+}
+.moderate-tags-page td.per-thread-controls {
+ width: 120px;
+ /* 20px more to compensate for the padding */
+
+ height: 30px;
+}
+.moderate-tags-page td.per-thread-controls button {
+ display: none;
+}
+.moderate-tags-page th.decision-col,
+.moderate-tags-page th.tags-col,
+.moderate-tags-page th.users-col {
+ width: 100px;
+}
+.moderate-tags-page tr.per-tag-controls {
+ height: 30px;
+ text-align: center;
+}
+.moderate-tags-page tr.thread-info a {
+ line-height: 18px;
+}
+.moderate-tags-page tr.thread-info td {
+ padding-bottom: 5px;
+}
+.moderate-tags-page td.tags-col,
+.moderate-tags-page td.users-col {
+ padding-top: 7px;
+}
+.moderate-tags-page td.thread-links-col {
+ padding-top: 5px;
+}
.main-page h1 {
margin-right: 5px;
}
@@ -1331,7 +1457,7 @@ ul#related-tags li {
color: #1b79bd;
border-top: #f0f0ec 1px solid;
border-left: #f0f0ec 1px solid;
- height: 30px;
+ min-height: 30px;
line-height: 30px;
font-weight: normal;
}
@@ -1358,7 +1484,191 @@ ul#related-tags li {
font-size: 13px;
}
.ask-page #id_tags,
-.edit-question-page #id_tags,
+.edit-question-page #id_tags {
+ border: #cce6ec 3px solid;
+ height: 25px;
+ padding-left: 5px;
+ font-size: 14px;
+ width: 395px;
+}
+.ask-page #id_post_author_username,
+.question-page #id_post_author_username,
+.edit-question-page #id_post_author_username,
+.edit-answer-page #id_post_author_username,
+.ask-page #id_post_author_email,
+.question-page #id_post_author_email,
+.edit-question-page #id_post_author_email,
+.edit-answer-page #id_post_author_email {
+ border: #cce6ec 3px solid;
+ height: 25px;
+ padding-left: 5px;
+ font-size: 14px;
+ width: 186px;
+}
+.ask-page #id_post_author_email,
+.question-page #id_post_author_email,
+.edit-question-page #id_post_author_email,
+.edit-answer-page #id_post_author_email {
+ margin-left: 10px;
+}
+.ask-page table.proxy-user-info,
+.question-page table.proxy-user-info,
+.edit-question-page table.proxy-user-info,
+.edit-answer-page table.proxy-user-info {
+ border-spacing: 0px;
+ width: 100%;
+}
+.ask-page table.proxy-user-info .form-item,
+.question-page table.proxy-user-info .form-item,
+.edit-question-page table.proxy-user-info .form-item,
+.edit-answer-page table.proxy-user-info .form-item {
+ float: left;
+}
+.groups-input,
+.users-input {
+ width: 152px;
+ padding-left: 5px;
+ border: #c9c9b5 1px solid;
+ height: 25px;
+ font-size: 14px;
+}
+.add-groups,
+.add-users {
+ border: 0;
+ font-weight: bold;
+ margin-top: -2px;
+ height: 27px;
+ font-size: 14px;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+ color: #4a757f;
+ font-family: 'Open Sans Condensed', 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;
+}
+.share-input-col {
+ width: 160px;
+ text-align: center;
+}
+.add-everyone-group {
+ text-align: center;
+ margin: auto;
+ display: block;
+ padding: 0 10px;
+ height: 25px;
+}
+.add-groups: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;
+}
+#id_user,
+#id_user_author {
+ border: #cce6ec 3px solid;
+ height: 25px;
+ padding-left: 5px;
+ width: 395px;
+ font-size: 14px;
+}
+.groups-input,
+.users-input {
+ width: 152px;
+ padding-left: 5px;
+ border: #c9c9b5 1px solid;
+ height: 25px;
+ font-size: 14px;
+}
+.add-groups,
+.add-users {
+ border: 0;
+ font-weight: bold;
+ margin-top: -2px;
+ height: 27px;
+ font-size: 14px;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+ color: #4a757f;
+ font-family: 'Open Sans Condensed', 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;
+}
+.add-everyone-group {
+ text-align: center;
+ margin: auto;
+ display: block;
+ padding: 0 10px;
+}
+.add-groups: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;
+}
#id_user,
#id_user_author {
border: #cce6ec 3px solid;
@@ -1372,6 +1682,14 @@ ul#related-tags li {
font-size: 13px;
margin-bottom: 5px;
}
+.ask-page .title-desc,
+.question-page .title-desc,
+.ask-page .tags-desc,
+.question-page .tags-desc {
+ color: #707070;
+ font-style: italic;
+ font-size: 16px;
+}
#fmanswer input.submit,
.ask-page input.submit,
.edit-question-page input.submit {
@@ -1428,6 +1746,10 @@ ul#related-tags li {
}
.wmd-container {
border: #cce6ec 3px solid;
+ min-height: 250px;
+}
+.wmd-container textarea {
+ border: none;
}
.users-page .wmd-container {
width: 200px;
@@ -1445,6 +1767,12 @@ ul#related-tags li {
width: 710px;
padding: 6px;
}
+.ask-page .retagger-buttons button,
+.question-page .retagger-buttons button,
+.edit-question-page .retagger-buttons button,
+.edit-answer-page .retagger-buttons button {
+ margin: 8px 10px 5px 0;
+}
#editor {
/* adjustment for editor preview */
@@ -1590,6 +1918,12 @@ ul#related-tags li {
width: 682px;
margin-bottom: 10px;
}
+.question-page .question-content pre,
+.question-page .answer pre,
+.question-page .question-content code,
+.question-page .answer code {
+ clear: both;
+}
.question-page #question-table {
float: left;
border-top: #f0f0f0 1px solid;
@@ -1742,6 +2076,41 @@ ul#related-tags li {
.question-page .answer-controls .permant-link {
background: url(../images/link.png) no-repeat center left;
}
+.question-page .post-controls .answer-convert,
+.question-page .answer-controls .answer-convert {
+ float: right;
+ clear: left;
+ /*background: url(../images/link.png) no-repeat center left;*/
+
+}
+.question-page .post-controls .answer-convert input,
+.question-page .answer-controls .answer-convert input {
+ font-size: 12px;
+ color: #777;
+ font-family: Arial;
+ text-decoration: none;
+ display: inline;
+ white-space: nowrap;
+ padding-left: 0px;
+ background: none;
+ border: none;
+ padding: 0px 7px 3px 18px;
+ float: right;
+ height: 18px;
+ line-height: 18px;
+ margin-top: -2px;
+ margin-left: 4px;
+ box-shadow: none;
+}
+.question-page .post-controls .answer-convert input:hover,
+.question-page .answer-controls .answer-convert input:hover {
+ background-color: #f5f0c9;
+ border-radius: 3px;
+ -ms-border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ -khtml-border-radius: 3px;
+}
.question-page .tabBar {
width: 100%;
}
@@ -1781,9 +2150,6 @@ ul#related-tags li {
.question-page #fmanswer_button {
margin: 8px 0px;
}
-.question-page #fmanswer_button.answer-own-question {
- width: 150px;
-}
.question-page .question-img-favorite:hover {
background: url(../images/vote-favorite-on.png);
}
@@ -1975,6 +2341,27 @@ ul#related-tags li {
.question-page .comments .comment-body .edit {
padding-left: 6px;
}
+.question-page .comments .comment-body .convert-comment {
+ display: inline;
+ white-space: nowrap;
+ padding-left: 0px;
+}
+.question-page .comments .comment-body .convert-comment input {
+ background: none;
+ padding: 0px;
+ color: #1B79BD;
+ border: none;
+ width: auto;
+ font-family: Arial;
+ line-height: 14px;
+ margin-left: 6px;
+ font-size: 13px;
+ box-shadow: none;
+}
+.question-page .comments .comment-body .convert-comment input:hover {
+ text-decoration: underline;
+ cursor: pointer;
+}
.question-page .comments .comment-body p {
font-size: 13px;
line-height: 1.3;
@@ -2071,6 +2458,7 @@ ul#related-tags li {
}
.question-page .answer .vote-buttons {
float: left;
+ margin-top: 10px;
}
.question-page .accepted-answer {
background-color: #f7fecc;
@@ -2311,6 +2699,26 @@ ul#related-tags li {
.user-profile-page .cancel:hover {
background: url(../images/small-button-cancel.png) repeat-x bottom !important;
}
+.openid-signin .re,
+.meta .re,
+.users-page .re,
+.user-profile-edit-page .re,
+.user-profile-page .re {
+ float: left;
+ width: 960px;
+}
+.inbox-flags.user-profile-page .re {
+ width: 810px;
+}
+.inbox-flags.user-profile-page .post-moderation-controls {
+ float: left;
+ width: 150px;
+ margin-top: 23px;
+ text-align: right;
+}
+.inbox-flags.user-profile-page .dropdown:hover ul.dropdown-menu {
+ display: block;
+}
.openid-signin form {
margin-bottom: 5px;
}
@@ -2421,6 +2829,9 @@ ul#related-tags li {
/*.users-page .tabBar{
width:375px;
}*/
+.users-page #group-openness-selector {
+ width: 200px;
+}
.user {
padding: 5px 10px 5px 0;
line-height: 140%;
@@ -2443,7 +2854,6 @@ ul#related-tags li {
}
/* tags page */
.tabBar-tags {
- width: 270px;
margin-bottom: 15px;
}
/* badges page */
@@ -2801,7 +3211,7 @@ ins {
width: 100%;
clear: both;
border-top: 1px solid #000;
- padding: 6px 0 0 0;
+ padding: 16px 0 0 0;
background: #16160f;
font-size: 16px;
font-family: 'Open Sans Condensed', Arial, sans-serif;
@@ -2812,14 +3222,14 @@ ins {
.footer-links {
color: #EEE;
text-align: left;
- width: 500px;
+ width: 450px;
float: left;
}
.footer-links a {
color: #e7e8a8;
}
.powered-link {
- width: 500px;
+ width: 450px;
float: left;
text-align: left;
}
@@ -2828,7 +3238,7 @@ ins {
}
.copyright {
color: #616161;
- width: 450px;
+ width: 500px;
float: right;
text-align: right;
}
@@ -2883,9 +3293,6 @@ table.form-as-table th {
table.ab-subscr-form {
width: 45em;
}
-table.ab-tag-filter-form {
- width: 45em;
-}
.submit-row {
line-height: 30px;
padding-top: 10px;
@@ -2896,10 +3303,16 @@ table.ab-tag-filter-form {
line-height: 20px;
color: red;
}
-.error {
+.error,
+.openid-signin p.error {
color: darkred;
margin: 0;
- font-size: 10px;
+ font-size: 12px;
+ font-weight: bold;
+ text-align: left;
+}
+.openid-signin p.error {
+ text-align: center;
}
label.retag-error {
color: darkred;
@@ -3299,6 +3712,9 @@ p.signup_p {
list-style-position: outside;
margin: 0;
}
+.simple-subscribe-options input {
+ display: inline;
+}
/* a workaround to set link colors correctly */
.wmd-preview a {
color: #1b79bd;
@@ -3394,38 +3810,41 @@ a.edit {
padding-left: 3px;
color: #145bff;
}
-.str {
+pre {
+ /* name conflict here with tags */
+
+}
+pre .str {
color: #080;
}
-.kwd {
+pre .kwd {
color: #008;
}
-.com {
+pre .com {
color: #800;
}
-.typ {
+pre .typ {
color: #606;
}
-.lit {
+pre .lit {
color: #066;
}
-.pun {
+pre .pun {
color: #660;
}
-.pln {
+pre .pln {
color: #000;
}
-.tag {
+pre .tag {
color: #008;
}
-/* name conflict here */
-.atn {
+pre .atn {
color: #606;
}
-.atv {
+pre .atv {
color: #080;
}
-.dec {
+pre .dec {
color: #606;
}
pre.prettyprint {
@@ -3434,38 +3853,38 @@ pre.prettyprint {
border: 0px solid #888;
}
@media print {
- .str {
+ pre .str {
color: #060;
}
- .kwd {
+ pre .kwd {
color: #006;
font-weight: bold;
}
- .com {
+ pre .com {
color: #600;
font-style: italic;
}
- .typ {
+ pre .typ {
color: #404;
font-weight: bold;
}
- .lit {
+ pre .lit {
color: #044;
}
- .pun {
+ pre .pun {
color: #440;
}
- .pln {
+ pre .pln {
color: #000;
}
- .tag {
+ pre .tag {
color: #006;
font-weight: bold;
}
- .atn {
+ pre .atn {
color: #404;
}
- .atv {
+ pre .atv {
color: #060;
}
}
@@ -3511,6 +3930,11 @@ body.anon.lang-es #searchBar .searchInputCancelable {
color: white;
background: #b32f2f;
}
+.question-page .post-update-info a.primary-group-name,
+a.primary-group-name {
+ color: #990E08;
+ font-weight: bold;
+}
.users-page .wmd-prompt-dialog {
background: #ccc;
}
@@ -3542,6 +3966,17 @@ img.group-logo {
#groups-list td {
padding-bottom: 5px;
}
+.groups-page #groups-list th,
+.groups-page #groups-list td {
+ padding-right: 20px;
+}
+.groups-page #groups-list th {
+ font-weight: bold;
+}
+.groups-page #groups-list th:nth-child(2),
+.groups-page #groups-list td:nth-child(2) {
+ text-align: center;
+}
#reject-edit-modal input,
#reject-edit-modal textarea {
width: 514px;
@@ -3563,6 +3998,13 @@ textarea.tipped-input {
font-size: 14px;
line-height: 25px;
}
+.select-box li input {
+ margin: 0 0 2px -5px;
+ font-size: 14px;
+ line-height: 14px;
+ vertical-align: middle;
+ color: #707070;
+}
.select-box li.selected,
.select-box li.selected:hover {
background-color: #fcf8e3;
@@ -3572,6 +4014,84 @@ textarea.tipped-input {
background-color: #cecece;
color: white;
}
+/* category selector */
+.category-selector {
+ border-spacing: 0;
+}
+.category-selector ul.select-box {
+ height: 150px;
+ width: 235px;
+ overflow: auto;
+ border: #ccc 3px solid;
+}
+.category-selector td {
+ vertical-align: top;
+}
+.category-selector li {
+ position: relative;
+ color: #707070;
+}
+.category-selector li.tree:after {
+ content: ">>";
+ position: absolute;
+ right: 5px;
+ font-weight: bold;
+}
+.category-selector li.selected.tree:after {
+ color: #C09853;
+}
+.category-selector th {
+ color: #707070;
+ font-style: italic;
+ font-size: 16px;
+ font-weight: normal;
+ padding-top: 5px;
+ text-align: left;
+}
+.question-page .category-selector ul.select-box {
+ width: 217px;
+}
+.question-page .category-selector ul.select-box input {
+ width: 95px;
+}
+.question-page .tag-editor {
+ width: 660px;
+ margin-left: 0;
+}
+.editor-status {
+ float: right;
+ margin: 7px 350px 0 0;
+ font-weight: bold;
+}
+.editor-status span {
+ display: none;
+}
+/* tag editor */
+.tag-editor {
+ height: 64px;
+ border: #ccc 3px solid;
+ padding-left: 8px;
+}
+.tag-editor ul.tags {
+ margin: 0;
+}
+.tag-editor ul.tags li {
+ margin-top: 8px;
+ height: 13px;
+}
+.tag-editor input.new-tags-input,
+.tag-editor input.new-tags-input:focus {
+ border: none;
+ font-size: 15px;
+ font-color: #707070;
+ line-height: 16px;
+ margin-top: 9px;
+ -webkit-box-shadow: none;
+ /* undo bootstrap glow */
+
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
/* fixes for bootstrap */
.caret {
margin-bottom: 7px;
diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less
index 2b6a45bd..d898852e 100644
--- a/askbot/skins/default/media/style/style.less
+++ b/askbot/skins/default/media/style/style.less
@@ -39,6 +39,13 @@ input, select {
margin-left:0px;
}
+input[type="text"].prompt,
+input[type="password"].prompt,
+input.tipped-input.blank {
+ font-style: italic;
+ color: @info-text;
+}
+
textarea:focus, input:focus{
outline: none;
}
@@ -187,7 +194,7 @@ body.user-messages {
.notification {
margin-top: 6px;
- margin-bottom: 6px;
+ /*margin-bottom: 6px;*/
font-size: 16px;
color:#424242
}
@@ -274,7 +281,7 @@ body.user-messages {
a {
color: #e2e2ae;
padding: 0px 0px 0px 35px;
- height: 25px;
+ height: 35px;
line-height: 25px;
margin:5px 0px 0px 10px;
font-size: 18px;
@@ -306,20 +313,66 @@ body.user-messages {
}
#navTags{
- .sprites(-50px,-5px)
- }
-
- #navUsers{
- .sprites(-125px,-5px)
+ .sprites(0px,-95px);
}
+ #navUsers,
#navGroups{
- .sprites(-125px,-5px)
+ .sprites(3px,-133px)
}
#navBadges{
- .sprites(-210px,-5px)
+ .sprites(3px,-170px)
+ }
+
+ a.group-name {
+ padding: 0px;
+ float:center;
+ margin:5px 0px 5px 10px;
+ }
+
+ input.group-name{
+ border:none;
+ height: 25px;
+ font-size: 18px;
+ font-weight: 100;
+ text-decoration: none;
+ display: block;
+ margin: 0px 10px 0px 10px;
+ width: 140px;
+ font-family: 'Open Sans Condensed', Arial, sans-serif;
+ font-weight: 100;
}
+
+ input.group-name:focus{
+ border:none;
+ }
+
+ a.group-name:hover{
+ background-color: transparent;
+ }
+
+ span.dropdown:hover ul.dropdown-menu {
+ display: block;
+ }
+
+ div.dropdown-container:hover ul.dropdown-menu {
+ display: block;
+ }
+
+ .dropdown {
+ float:left;
+ }
+
+ .dropdown-menu{
+ border-top: none;
+ left: 7%;
+ a{
+ color: #666;
+ height: 25px;
+ }
+ }
+
}
#header.with-logo #userToolsNav {
@@ -333,9 +386,6 @@ body.user-messages {
margin-top: 7px;
}
-#header.without-logo #metaNav {
- margin-bottom: 7px;
-}
#secondaryHeader{ /* Div containing Home button, scope navigation, search form and ask button, check blocks/secondary_header.html */
height:55px;
@@ -384,6 +434,25 @@ body.user-messages {
}
}
+.validate-email-page {
+ label {
+ color: @info-text;
+ line-height: 1.35;
+ display: block;
+ margin: 10px 0;
+ }
+ #validation-code {
+ padding-left:5px;
+ border:#cce6ec 3px solid;
+ height:25px;
+ font-size: 14px;
+ width: 200px;
+ }
+ form {
+ margin-bottom: 30px;
+ }
+}
+
#searchBar { /* Main search form , check widgets/search_bar.html */
display: inline-block;
background-color: #fff;
@@ -512,6 +581,7 @@ body.anon {
margin-bottom: 4px;
color: @info-text;
font-family:@main-font;
+ font-size: 14px;
}
p.info-box-follow-up-links {
@@ -545,6 +615,10 @@ body.anon {
background: #eceeeb url(../images/contributorsback.png) no-repeat center left;
}
+ form {
+ margin: 0px;
+ }
+
label {
color: @info-text;
font-size:15px;
@@ -1234,7 +1308,6 @@ ul#related-tags li {
}
h1 {
float: left;
- padding-top: 7px;
}
}
@@ -1430,6 +1503,7 @@ ul#related-tags li {
}
table.proxy-user-info {
border-spacing: 0px;
+ width: 100%;
.form-item {
float: left;
@@ -1455,6 +1529,50 @@ ul#related-tags li {
.rounded-corners(4px);
}
+.share-input-col {
+ width: 160px;
+ text-align: center;
+}
+
+.add-everyone-group {
+ text-align: center;
+ margin: auto;
+ display: block;
+ padding: 0 10px;
+ height: 25px;
+}
+
+.add-groups:hover {
+ .button-style-hover;
+}
+
+#id_user,
+#id_user_author {
+ border:#cce6ec 3px solid;
+ height:25px;
+ padding-left:5px;
+ width:395px;
+ font-size:14px;
+}
+
+.groups-input,
+.users-input {
+ width:152px;
+ padding-left:5px;
+ border:#c9c9b5 1px solid;
+ height:25px;
+ font-size: 14px;
+}
+
+.add-groups,
+.add-users {
+ border:0;
+ font-weight:bold;
+ margin-top:-2px;
+ .button-style(27px, 14px);
+ .rounded-corners(4px);
+}
+
.add-everyone-group {
text-align: center;
margin: auto;
@@ -1584,6 +1702,25 @@ ul#related-tags li {
max-width: 600px;
}
+.defaultSkin table.mceLayout,
+.defaultSkin table.mceLayout tr.mceFirst td {
+ border: none;
+}
+.defaultSkin table.mceLayout tr.mceLast td {
+ border-bottom: none;
+}
+.mceStatusbar {
+ height: 5px;
+ background: #fff;
+}
+.defaultSkin span.mce_askbot_imageuploader {
+ background-position: -380px 0px;
+}
+.defaultSkin span.mce_askbot_attachment {
+ background-image: url(../images/attachment.png);
+ background-position: 0px 0px;
+}
+
.user-page .wmd-buttons {
width: 725px;
}
@@ -1827,14 +1964,20 @@ ul#related-tags li {
}
.post-controls, .answer-controls{
.question-delete{
- background: url(../images/delete.png) no-repeat center left;
+ background: url(../images/delete.png) no-repeat left 2px;
padding-left:11px;
}
.question-flag{
background: url(../images/flag.png) no-repeat center left;
}
+ .answer-publish{
+ background: url(../images/publish.png) no-repeat center left;
+ }
+ .answer-unpublish{
+ background: url(../images/unpublish.png) no-repeat 2px center;
+ }
.question-edit{
- background: url(../images/edit2.png) no-repeat center left;
+ background: url(../images/edit2.png) no-repeat 2px center;
}
.question-retag{
background: url(../images/retag.png) no-repeat center left;
@@ -1845,6 +1988,36 @@ ul#related-tags li {
.permant-link{
background: url(../images/link.png) no-repeat center left;
}
+
+ .answer-convert{
+ float:right;
+ clear: left;
+ /*background: url(../images/link.png) no-repeat center left;*/
+ }
+
+ .answer-convert input{
+ font-size:12px;
+ color: #777;
+ font-family:@body-font;
+ text-decoration: none;
+ display:inline;
+ white-space:nowrap;
+ padding-left: 0px;
+ background: none;
+ border: none;
+ padding: 0px 7px 3px 18px;
+ float:right;
+ height:18px;
+ line-height:18px;
+ margin-top:-2px;
+ margin-left:4px;
+ box-shadow: none;
+ }
+
+ .answer-convert input:hover{
+ background-color: #f5f0c9;
+ .rounded-corners(3px);
+ }
}
.tabBar{
width:100%;
@@ -2040,6 +2213,30 @@ ul#related-tags li {
.edit{
padding-left:6px;
}
+
+ .convert-comment{
+ display: inline;
+ white-space: nowrap;
+ padding-left: 0px;
+ }
+
+ .convert-comment input{
+ background: none;
+ padding: 0px;
+ color: #1B79BD;
+ border:none;
+ width:auto;
+ font-family: Arial;
+ line-height: 14px;
+ margin-left: 6px;
+ font-size: 13px;
+ box-shadow: none;
+ }
+
+ .convert-comment input:hover{
+ text-decoration:underline;
+ cursor:pointer;
+ }
}
.comment-body p{
@@ -2418,6 +2615,10 @@ ul#related-tags li {
width:375px;
}*/
+.users-page #group-openness-selector {
+ width: 200px;
+}
+
.user {
padding: 5px 10px 5px 0;
line-height: 140%;
@@ -3638,6 +3839,19 @@ img.group-logo {
}
}
+.groups-page #groups-list {
+ th, td {
+ padding-right: 20px;
+ }
+ th {
+ font-weight: bold;
+ }
+ th:nth-child(2),
+ td:nth-child(2) {
+ text-align: center;
+ }
+}
+
#reject-edit-modal {
input, textarea {
width: 514px;
@@ -3764,3 +3978,24 @@ textarea.tipped-input {
box-shadow: none;
}
}
+
+/* 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/500.html b/askbot/skins/default/templates/500.html
deleted file mode 100644
index 8ec1bce4..00000000
--- a/askbot/skins/default/templates/500.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% load extra_tags %}
-{% include_jinja "500.jinja.html" request %}
-{% comment %}this template must be django
-because of the use of default handler500
-{% endcomment %}
diff --git a/askbot/skins/default/templates/answer_edit.html b/askbot/skins/default/templates/answer_edit.html
index b38f3fff..8c3687f1 100644
--- a/askbot/skins/default/templates/answer_edit.html
+++ b/askbot/skins/default/templates/answer_edit.html
@@ -59,8 +59,6 @@
{% if settings.EDITOR_TYPE == 'markdown' %}
<script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/wmd/wmd.js"|media}}'></script>
- {% else %}
- {% include "meta/tinymce.html" %}
{% endif %}
<script type="text/javascript">
$().ready(function(){
diff --git a/askbot/skins/default/templates/ask.html b/askbot/skins/default/templates/ask.html
index 5f072577..27434f83 100644
--- a/askbot/skins/default/templates/ask.html
+++ b/askbot/skins/default/templates/ask.html
@@ -19,9 +19,6 @@
{% if settings.EDITOR_TYPE == 'markdown' %}
<script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/wmd/wmd.js"|media}}'></script>
- {% else %}
- <script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script>
- {% include "meta/tinymce.html" %}
{% endif %}
<script type='text/javascript'>
var sortMethod = undefined;//need for live_search
diff --git a/askbot/skins/default/templates/embed/ask_by_widget.html b/askbot/skins/default/templates/embed/ask_by_widget.html
new file mode 100644
index 00000000..4cec5f6d
--- /dev/null
+++ b/askbot/skins/default/templates/embed/ask_by_widget.html
@@ -0,0 +1,225 @@
+{% extends "widget_base.html" %}
+{% import "macros.html" as macros %}
+{% block forestyle %}
+ {% if editor_type == 'markdown' %}
+ <link rel="stylesheet" type="text/css" href="{{"/js/wmd/wmd.css"|media}}" />
+ {%else %}
+ <link href="{{'/bootstrap/css/bootstrap.css'|media}}" rel="stylesheet" type="text/css" />
+ {%endif%}
+ {% if settings.USE_LOCAL_FONTS %}
+ {% include "meta/fonts.html" %}
+ {% else %}
+ <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:400,700&amp;subset=latin,cyrillic-ext,latin-ext' rel='stylesheet' type='text/css' />
+ {% endif %}
+ <style type="text/css" media="screen">
+ body{
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ }
+ #editor {
+ display: block;
+ min-height: 200px;
+ width: 98%;
+ margin: 0;
+ border:none;
+ outline:none;
+ padding: 6px;
+ }
+ .wmd-container{
+ border: #CCE6EC 3px solid;
+ width: 100%;
+ margin-top: 20px;
+ }
+
+ #id_title{
+ {% if editor_type == 'markdown' %}
+ width: 100%;
+ {%else%}
+ padding: 0px 0 0 5px;
+ width: 99.2%;
+ {%endif%}
+ font-size: 130%;
+ border: #CCE6EC 3px solid;
+ max-width: 600px;
+ }
+
+ #id_title:focus {
+ outline: none;
+ box-shadow:none;
+ }
+
+ .title{
+ font-family: 'Open Sans Condensed', Arial, sans-serif;
+ font-size: 25px;
+ margin-bottom: 10px;
+ }
+
+ #question-list {
+ height: auto;
+ z-index: 10000;
+ position: absolute;
+ border: 3px solid #CCE6EC;
+ border-top:none;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ background: #fff;
+ {% if editor_type == 'markdown' %}
+ width:97.4%;
+ {%else%}
+ width:90.8%;
+ {%endif%}
+ margin-top: -4.9px;
+ }
+
+ #question-list h2{
+ text-decoration: none;
+ margin: 0px;
+ font-size: 13px;
+ padding: 3px 0 3px 5px;
+ padding-bottom: 0;
+ border-top: #F0F0EC 1px solid;
+ min-height: 30px;
+ line-height: 30px;
+ font-weight: normal
+ }
+
+ #question-list h2:first-child{
+ border-top: none;
+ }
+
+ #question-list a{
+ color: #005580;
+ }
+
+ #question-list a:hover{
+ color: #005580;
+ text-decoration: underline;
+ }
+
+ #question-list a:visited{
+ color: #005580;
+ text-decoration: none;
+ }
+
+ #question-list span{
+ width: 28px;
+ height: 26px;
+ line-height: 26px;
+ text-align: center;
+ margin-right: 10px;
+ float: left;
+ display: block;
+ color: white;
+ background: #B8D0D5;
+ border-radius: 3px;
+ -ms-border-radius: 3px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ -khtml-border-radius: 3px;
+ }
+ {% if editor_type == 'markdown' %}
+ #wmd-button-bar {
+ background: url({{"/js/wmd/images/editor-toolbar-background.png"|media}}) repeat-x bottom;
+ height: 30px;
+ border: 0;
+ display: block;
+ }
+ {% endif %}
+
+ #submit{
+ font-size: 130%;
+ margin-right: -6px;
+ float: right;
+ text-align: center;
+ text-decoration: none;
+ cursor: pointer;
+ color: #4A757F;
+ font-family: 'Open Sans Condensed', 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;
+ width: 200px;
+ }
+
+ .field-errors{
+ font-size: 10px;
+ color: #ff0000;
+ height: 10px;
+ }
+ {{widget.inner_style}}
+ </style>
+{%endblock%}
+
+{%block body%}
+<div class="title">{{widget.title}}</div>
+<form action="." method="POST" accept-charset="utf-8">
+ {% csrf_token %}
+ <label>{%trans%}Please enter a descriptive title for your question{%endtrans%}</label>
+ <div class="input-title">
+ {{form.title}}
+ </div>
+ <div id='question-list'>
+ </div>
+ <div class="field-errors">
+ {% for error in form.title.errors %} {{error}} {%endfor%}
+ </div>
+{% if widget.include_text_field %}
+ {% if editor_type == 'markdown' %}
+ <div class="wmd-container">
+ <div id="wmd-button-bar" class="wmd-panel"></div>
+ {{ form.text }}{# this element is resizable and will be wrapped by js #}
+ </div>
+ {% else %}
+ <div class="wmd-container">
+ {{ form.media }}
+ {{ form.text }}
+ </div>
+ {% endif %}
+ <div class="field-errors">
+ {% for error in form.text.errors %} {{error}} {%endfor%}
+ </div>
+ {% endif %}
+ {% if form.ask_anonymously %}
+ <p>{{form.ask_anonymously.label_tag()}}: {{form.ask_anonymously}}</p>
+ {%endif%}
+ <input type="submit" value="Ask your question" id="submit" />
+</form>
+{%endblock%}
+{% block endjs %}
+<script type='text/javascript' src='{{"/js/editor.js"|media}}'></script>
+{% if editor_type == 'markdown' %}
+ <script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script>
+ <script type='text/javascript' src='{{"/js/wmd/wmd.js"|media}}'></script>
+{% else %}
+ {% include "meta/tinymce_css.html" %}{# todo - maybe move to form media? #}
+{% endif %}
+
+<script type="text/javascript" src='{{"/js/live_search_new_thread.js"|media}}'></script>
+<script type="text/javascript" charset="utf-8">
+ var minSearchWordLength = {{settings.MIN_SEARCH_WORD_LENGTH}};
+ askbot['urls']['api_get_questions'] = '{% url api_get_questions %}';
+ askbot['urls']['upload'] = '{% url upload %}';
+ $(document).ready(function(){
+ $("#id_title").focus();
+ $("#id_title").addClass('questionTitleInput');
+ liveSearchNewThreadInit(true);
+ });
+</script>
+{% endblock %}
diff --git a/askbot/skins/default/templates/embed/ask_widget_complete.html b/askbot/skins/default/templates/embed/ask_widget_complete.html
new file mode 100644
index 00000000..580c1f94
--- /dev/null
+++ b/askbot/skins/default/templates/embed/ask_widget_complete.html
@@ -0,0 +1,8 @@
+{% extends "widget_base.html" %}
+{% block forestyle %}
+{%endblock%}
+
+{%block body%}
+<a href="{{question_url}}" target="_blank" >Question posted</a>
+{%endblock%}
+
diff --git a/askbot/skins/default/templates/embed/askbot_widget.css b/askbot/skins/default/templates/embed/askbot_widget.css
new file mode 100755
index 00000000..9b0b5af2
--- /dev/null
+++ b/askbot/skins/default/templates/embed/askbot_widget.css
@@ -0,0 +1,38 @@
+#{{variable_name}} {
+ visibility: hidden;
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width:100%;
+ height:100%;
+ text-align:center;
+ z-index: 99999;
+}
+
+#{{variable_name}} div{
+ width:600px;
+ {%if widget.include_text_field%}
+ {% if editor_type == 'markdown' %}
+ height:520px;
+ {%else%}
+ height:520px;
+ {%endif%}
+ {%else%}
+ height:220px;
+ {%endif%}
+ margin: 100px auto;
+ background-color: #fff;
+ border:1px solid #000;
+ padding:15px;
+ padding-left: 10px;
+ text-align: right;
+}
+
+#{{variable_name}} a{
+ padding:5px;
+}
+
+#{{variable_name}} iframe{
+ width:600px;
+ height:500px;
+}
diff --git a/askbot/skins/default/templates/embed/askbot_widget.js b/askbot/skins/default/templates/embed/askbot_widget.js
new file mode 100755
index 00000000..8c358855
--- /dev/null
+++ b/askbot/skins/default/templates/embed/askbot_widget.js
@@ -0,0 +1,74 @@
+var {{variable_name}} = {
+ element_id: "{{variable_name}}",
+ widgetToggle: function() {
+ element = document.getElementById({{variable_name}}.element_id);
+ element.style.visibility = (element.style.visibility == "visible") ? "hidden" : "visible";
+ if (element.style.visibility == "visible"){
+ $("#" + {{variable_name}}.element_id + " iframe").focus();
+ }
+ },
+ toHtml: function() {
+ var html = {{variable_name}}.createButton();
+ var link = document.createElement('link');
+ link.setAttribute("rel", "stylesheet");
+ link.setAttribute("href", 'http://{{host}}{%url render_ask_widget_css widget.id%}');
+
+ //creating the div
+ var motherDiv = document.createElement('div');
+ motherDiv.setAttribute("id", {{variable_name}}.element_id);
+ motherDiv.style.visibility = "hidden";
+
+ var containerDiv = document.createElement('div');
+ motherDiv.appendChild(containerDiv);
+
+ {%if widget.outer_style %}
+ outerStyle = document.createElement('style');
+ outerStyle.innerText = "{{widget.outer_style}}";
+ motherDiv.appendChild(outerStyle);
+ {%endif%}
+
+ var closeButton = document.createElement('a');
+ closeButton.setAttribute('href', '#');
+ closeButton.setAttribute('id', 'AskbotModalClose');
+ closeButton.setAttribute('onClick', '{{variable_name}}.widgetToggle();');
+ closeButton.innerHTML= 'Close';
+
+ containerDiv.appendChild(closeButton);
+
+ var iframe = document.createElement('iframe');
+ iframe.setAttribute('src', 'http://{{host}}{% url ask_by_widget widget.id %}');
+
+ containerDiv.appendChild(iframe);
+
+ var body = document.getElementsByTagName('body')[0];
+ if (body){
+ body.appendChild(link);
+ body.appendChild(motherDiv);
+ }
+ },
+ createButton: function() {
+ var label="{{widget.title}}"; //TODO: add to the model
+ var buttonDiv = document.createElement('div');
+ buttonDiv.setAttribute('id', "AskbotAskButton");
+
+ var closeButton = document.createElement('input');
+ closeButton.setAttribute('onClick', '{{variable_name}}.widgetToggle();');
+ closeButton.setAttribute('type', 'button');
+ closeButton.value = label;
+
+ buttonDiv.appendChild(closeButton);
+
+ return buttonDiv;
+ }
+};
+
+previous_function = window.onload;
+var onload_functions = function(){
+ if (previous_function){
+ previous_function();
+ }
+ {{variable_name}}.toHtml();
+}
+
+window.onload = onload_functions();
+document.write({{variable_name}}.createButton().outerHTML);
diff --git a/askbot/skins/default/templates/embed/delete_widget.html b/askbot/skins/default/templates/embed/delete_widget.html
new file mode 100644
index 00000000..ed80c537
--- /dev/null
+++ b/askbot/skins/default/templates/embed/delete_widget.html
@@ -0,0 +1,14 @@
+{% extends "one_column_body.html" %}
+<!-- create_ask_widget.html -->
+{% block title %}Delete {{widget_name|capitalize}} Widget{% endblock %}
+{% block content %}
+<h1>Are you sure that you cant to delete this {{widget_name|capitalize}}Widget?</h1>
+<br/>
+<strong>Warning: This could break the widgets on sites that currently use this widget please make sure that you don't use the widget in other sites</strong>
+<form action="." method="POST">
+ <p><input type='submit' value='Delete' /> <a href="{% url list_widgets widget_name %}">Go Back</a></p>
+</form>
+{% endblock %}
+{% block endjs %}
+{% endblock %}
+
diff --git a/askbot/skins/default/templates/embed/list_widgets.html b/askbot/skins/default/templates/embed/list_widgets.html
new file mode 100644
index 00000000..83de5871
--- /dev/null
+++ b/askbot/skins/default/templates/embed/list_widgets.html
@@ -0,0 +1,45 @@
+{% extends "two_column_body.html" %}
+<!-- create_ask_widget.html -->
+{% block title %}{{widget_name|capitalize}} widget list{% endblock %}
+{% block content %}
+ <h1>{{widget_name|capitalize}} widget list</h1>
+
+<table border="0">
+ <tr>
+ <th>Widget Title </th>
+ <th>Code</th>
+ <th>Actions</th>
+ </tr>
+ {% if widget_name == 'ask' %}
+ {%for widget in widgets%}
+ <tr>
+ <td>{{widget.title}}</td>
+ <td> &lt;script type="text/javascript" src="http://{{request.get_host()}}{% url render_ask_widget widget.id%}" &gt;&lt;/script&gt;</td>
+ <td><a href="{% url edit_widget widget_name, widget.id %}">Edit</a> | <a href="{% url delete_widget widget_name, widget.id %}">Delete</a></td>
+ </tr>
+ {%endfor%}
+ {%else%}
+ {%for widget in widgets%}
+ <tr>
+ <td>{{widget.title}}</td>
+ <td> &lt;iframe src="http://{{request.get_host()}}{% url question_widget widget.id%}" &gt; &lt;/iframe&gt;</td>
+ <td><a href="{% url edit_widget widget_name, widget.id %}">Edit</a> | <a href="{% url delete_widget widget_name, widget.id %}">Delete</a></td>
+ </tr>
+ {%endfor%}
+
+ {%endif%}
+</table>
+
+{% endblock %}
+{% block endjs %}
+{% endblock %}
+
+{% block sidebar %}
+<div class="box">
+ <h2>{% trans %}How to use?{% endtrans %}</h2>
+ <p>{% trans %}
+ Just copy the &lt;script&gt; tag provided and paste it in the site where you wan to put it.
+ {%endtrans%}
+ </p>
+ </div>
+{% endblock %}
diff --git a/askbot/skins/default/templates/question_widget.html b/askbot/skins/default/templates/embed/question_widget.html
index 9d32294b..92e29aa0 100644
--- a/askbot/skins/default/templates/question_widget.html
+++ b/askbot/skins/default/templates/embed/question_widget.html
@@ -3,19 +3,18 @@
<html>
<head>
<style type="text/css">
- {{settings.QUESTIONS_WIDGET_CSS|safe}}
+ {{widget.style|safe}}
</style>
</head>
<body>
- {{settings.QUESTIONS_WIDGET_HEADER|safe}}
+ {{widget.title|safe}}
<div id="container">
<ul>
{% for thread in threads %}
- <li><a href="{{settings.APP_URL|strip_path}}{{ thread.get_absolute_url() }}">
+ <li><a href="{{settings.APP_URL|strip_path}}{{ thread.get_absolute_url() }}" target="_blank">
{{ thread.title|escape }}</a></li>
{% endfor %}
</ul>
</div>
- {{settings.QUESTIONS_WIDGET_FOOTER|safe}}
</body>
</html>
diff --git a/askbot/skins/default/templates/embed/widget_form.html b/askbot/skins/default/templates/embed/widget_form.html
new file mode 100644
index 00000000..65128d8e
--- /dev/null
+++ b/askbot/skins/default/templates/embed/widget_form.html
@@ -0,0 +1,23 @@
+{% extends "one_column_body.html" %}
+<!-- create_ask_widget.html -->
+{% block title %}{% trans %}{{action}} an {{widget_name}} widget{% endtrans %}{% endblock %}
+{% block content %}
+<h1 class="section-title">{% trans %}{{action}} an {{widget_name}} widget{% endtrans %}</h1>
+{#% if form.non_field_errors() %}
+ {{ form.non_field_errors() }}
+{% endif %#}
+<form method="post">
+ <table>
+ {{ form.as_table() }}
+ <tr>
+ <td colspan="2" style="text-align: center">
+ <input type="submit" class="submit" value={% trans %}Save{% endtrans %} />
+ </td>
+ </tr>
+ </table>
+</form>
+
+{% endblock %}
+{% block endjs %}
+{% endblock %}
+
diff --git a/askbot/skins/default/templates/embed/widgets.html b/askbot/skins/default/templates/embed/widgets.html
new file mode 100644
index 00000000..767ebc2c
--- /dev/null
+++ b/askbot/skins/default/templates/embed/widgets.html
@@ -0,0 +1,36 @@
+{% extends "one_column_body.html" %}
+<!-- template badges.html -->
+{% block title %}{% spaceless %}{% trans %}Widgets{% endtrans %}{% endspaceless %}{% endblock %}
+{% block content %}
+<h1 class="section-title">{% trans %}Widgets{% endtrans %}</h1>
+<p>
+</p>
+<table>
+ <thead>
+ <th colspan="3">
+ {% trans %}Create and embed widgets into your sites, here a list of available widgets.{% endtrans %}
+ </th>
+ </thead>
+ <tbody>
+ <tr>
+ <td>{% trans %}Ask a question{% endtrans %}</td>
+ <td><a href="{% url create_widget 'ask' %}">{% trans %}create{% endtrans %}</a></td>
+ <td>
+ {% if ask_widgets > 0 %}
+ <a href="{% url list_widgets 'ask' %}">{% trans %}view list{% endtrans %}</a>
+ {% endif %}
+ </td>
+ </tr>
+ <tr>
+ <td>{% trans %}List of questions{% endtrans %}</td>
+ <td><a href="{% url create_widget 'question' %}">{% trans %}create{% endtrans %}</a></td>
+ <td>
+ {% if question_widgets > 0 %}
+ <a href="{% url list_widgets 'question' %}">{% trans %}view list{% endtrans %}</a>
+ {% endif %}
+ </td>
+ </tr>
+ </tbody>
+</table>
+{% endblock %}
+<!-- end template badges.html -->
diff --git a/askbot/skins/default/templates/groups.html b/askbot/skins/default/templates/groups.html
index 2499ac9f..9c7dac3c 100644
--- a/askbot/skins/default/templates/groups.html
+++ b/askbot/skins/default/templates/groups.html
@@ -26,11 +26,21 @@
</p>
{% endif %}
<table id="groups-list">
- {% for group in groups %}
- <tr>
- {{ macros.user_group(group, groups_membership_info[group.id]) }}
- </tr>
- {% endfor %}
+ <thead>
+ <th>{% trans %}Group{% endtrans %}</th>
+ <th>{% trans %}Number of members{% endtrans %}</th>
+ <th>{% trans %}Description{% endtrans %}</th>
+ </thead>
+ <tbody>
+ {% for group in groups %}
+ <tr>
+ {{ macros.user_group(
+ group, groups_membership_info[group.id], show_count=True
+ )
+ }}
+ </tr>
+ {% endfor %}
+ </tbody>
</table>
{% endblock %}
{% block endjs %}
diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html
index ef5be144..8e578dec 100644
--- a/askbot/skins/default/templates/macros.html
+++ b/askbot/skins/default/templates/macros.html
@@ -17,7 +17,7 @@
{{ response.response_type }}
({{ timeago(response.timestamp) }}):<br/>
{% if inbox_section != 'flags' %}
- {{ response.response_snippet|escape }}
+ {{ response.response_snippet }}
{% endif %}
</a>
{% if inbox_section == 'flags' %}
@@ -207,25 +207,34 @@ poor design of the data or methods on data objects #}
</ul>
{%- endmacro -%}
-{%- macro user_group(group, membership_info) -%}
+{%- macro user_group_link(group) -%}
+ <a class="group-name"
+ href="{% url users_by_group group.id, group.name|replace('-', ' ')|slugify %}"
+ >{{ group.name|escape }}</a>
+{%- endmacro -%}
+
+{%- macro user_group(group, membership_info, show_count=False) -%}
<td>
- <a class="group-name"
- href="{% url users_by_group group.id, group.name|replace('-', ' ')|slugify %}"
- >{{ group.name|replace('-', ' ')|escape }}</a><br/>
- <span class="group-description">
- {% if group.tag_wiki %}
- {{ group.tag_wiki.summary }}
- {% endif %}
- </span>
- {% if membership_info %}
- <br/>
- {{ group_join_button(
- group_id = group.id,
- can_join = membership_info['can_join'],
- is_member = membership_info['is_member']
- )
- }}
- {% endif %}
+ {{ user_group_link(group) }}
+ </td>
+ {% if show_count %}
+ <td>{{ group.users_count }}</td>
+ {% endif %}
+ <td>
+ <span class="group-description">
+ {% if group.description %}
+ {{ group.description.summary }}
+ {% endif %}
+ </span>
+ {% if membership_info %}
+ <br/>
+ {{ group_join_button(
+ group_id = group.id,
+ acceptance_level = membership_info['aceptance_level'],
+ membership_level = membership_info['membership_level']
+ )
+ }}
+ {% endif %}
</td>
{%- endmacro -%}
@@ -239,21 +248,39 @@ poor design of the data or methods on data objects #}
{% endif %}
{%- endmacro -%}
-{%- macro group_join_button(group_id = None, can_join = False, is_member = False) -%}
- {% if can_join %}
+{%- macro group_join_button(
+ group_id=None, acceptance_level='closed', membership_level='none')
+-%}
+ {% if acceptance_level in ('open', 'moderated') %}
<button
- class="group-join-btn follow-toggle {% if is_member %}on on-state{% endif %}"
+ class="group-join-btn follow-toggle {% if membership_level != 'none' %}on on-state{% endif %}"
data-group-id="{{group_id}}"
- 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 acceptance_level == 'open' %}
+ 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 %}"
+ {% else %}
+ {% if membership_level == 'full' %}
+ data-off-prompt-text="{% trans %}Leave this group{% endtrans %}"
+ data-on-state-text="{% trans %}You are a member{% endtrans %}"
+ {% else %}
+ data-off-prompt-text="{% trans %}Cancel application{% endtrans %}"
+ data-on-state-text="{% trans %}Waiting approval{% endtrans %}"
+ {% endif %}
+ data-on-prompt-text="{% trans %}Ask to join{% endtrans %}"
+ data-off-state-text="{% trans %}Ask to join{% endtrans %}"
+ {% endif %}
>
- {% if is_member %}
+ {% if membership_level == 'full' %}
{% trans %}You are a member{% endtrans %}
+ {% elif membership_level == 'pending' %}
+ {% trans %}Waiting approval{% endtrans %}
{% else %}
- {% if can_join %}
+ {% if acceptance_level == 'open' %}
{% trans %}Join this group{% endtrans %}
+ {% else %}
+ {% trans %}Ask to join{% endtrans %}
{% endif %}
{% endif %}
</button>
@@ -397,9 +424,15 @@ for the purposes of the AJAX comment editor #}
<span class="age">&nbsp;({{ timeago(comment.added_at) }})</span>
<a id="post-{{comment.id}}-edit"
class="edit">{% trans %}edit{% endtrans %}</a>
+ <form action="{% url comment_to_answer %}" method="POST" accept-charset="utf-8" class='convert-comment'>
+ {% csrf_token %}
+ <input type="hidden" value="{{comment.id}}" name="comment_id" id="id_comment_id">
+ <input type="submit" value="{% trans %}convert to answer{% endtrans %}">
+ </form>
</div>
</div>
<script type="text/javascript">
+ askbot['functions']['hideConvertLinks']();
askbot['functions']['renderPostControls']('{{comment.id}}');
</script>
{% endfor %}
diff --git a/askbot/skins/default/templates/meta/bottom_scripts.html b/askbot/skins/default/templates/meta/bottom_scripts.html
index 76bef90b..b3fcd815 100644
--- a/askbot/skins/default/templates/meta/bottom_scripts.html
+++ b/askbot/skins/default/templates/meta/bottom_scripts.html
@@ -25,6 +25,7 @@
askbot['urls']['unfollow_user'] = '/followit/unfollow/user/{{'{{'}}userId{{'}}'}}/';
askbot['urls']['user_signin'] = '{{ settings.LOGIN_URL }}';
askbot['settings']['static_url'] = '{{ settings.STATIC_URL }}';
+ askbot['urls']['getEditor'] = '{% url "get_editor" %}';
</script>
<script
type="text/javascript"
@@ -61,6 +62,14 @@
if (askbot['data']['userIsAdminOrMod']) {
$('body').addClass('admin');
}
+ {%if settings.GROUPS_ENABLED %}
+ askbot['urls']['add_group'] = "{% url add_group %}";
+ var group_dropdown = new GroupDropdown({{group_list}});
+ $('.dropdown').append(group_dropdown.getElement());
+ {%if request.user.is_superuser%}
+ group_dropdown.enableAddGroups();
+ {%endif%}
+ {% endif %}
});
{% if user_messages %}
$('#validate_email_alert').click(function(){notify.close(true)})
diff --git a/askbot/skins/default/templates/meta/html_head_stylesheets.html b/askbot/skins/default/templates/meta/html_head_stylesheets.html
index 5f83cea1..85bb489c 100644
--- a/askbot/skins/default/templates/meta/html_head_stylesheets.html
+++ b/askbot/skins/default/templates/meta/html_head_stylesheets.html
@@ -1,3 +1,6 @@
+{%if settings.GROUPS_ENABLED%}
+<link href="{{'/bootstrap/css/bootstrap.css'|media}}" rel="stylesheet" type="text/css" />
+{% endif %}
{% if settings.ASKBOT_CSS_DEVEL == False %}
<link href="{{"/style/style.css"|media }}" rel="stylesheet" type="text/css" />
@@ -9,7 +12,9 @@
{% if settings.USE_LOCAL_FONTS %}
{% include "meta/fonts.html" %}
{% else %}
- <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:400,700&amp;subset=latin,cyrillic-ext,latin-ext' rel='stylesheet' type='text/css' />
+ {# note: IE8 fix - a combined font link wont work so we have two #}
+ <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:700&amp;subset=latin-ext' rel='stylesheet' type='text/css'>
+ <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:700&amp;subset=cyrillic-ext' rel='stylesheet' type='text/css'>
{% endif %}
{{ skin.get_extra_css_link() }}
{% if settings.USE_CUSTOM_CSS %}
diff --git a/askbot/skins/default/templates/meta/tinymce.html b/askbot/skins/default/templates/meta/tinymce_css.html
index d6b1b7b9..b6a1e798 100644
--- a/askbot/skins/default/templates/meta/tinymce.html
+++ b/askbot/skins/default/templates/meta/tinymce_css.html
@@ -1,4 +1,4 @@
-<style type="text/css">
+<style type="text/css">{# todo: clean up - this is also in style.less!!! #}
.defaultSkin table.mceLayout,
.defaultSkin table.mceLayout tr.mceFirst td {
border: none;
diff --git a/askbot/skins/default/templates/question.html b/askbot/skins/default/templates/question.html
index 06e29d8f..57c71068 100644
--- a/askbot/skins/default/templates/question.html
+++ b/askbot/skins/default/templates/question.html
@@ -8,11 +8,13 @@
{% block forestyle %}
<link rel="canonical" href="{{settings.APP_URL|strip_path}}{{question.get_absolute_url()}}" />
<link rel="stylesheet" type="text/css" href="{{'/js/wmd/wmd.css'|media}}" />
+ <link href="{{'/bootstrap/css/bootstrap.css'|media}}" rel="stylesheet" type="text/css" />
{% endblock %}
{% block forejs %}
<script type="text/javascript">
/*<![CDATA[*/
//below is pure cross-browser javascript, no jQuery
+ askbot['data']['userIsThreadModerator'] = {% if user_is_thread_moderator %}true{% else %}false{% endif %};
(function(){
var data = askbot['data'];
if (data['userIsAuthenticated']){
@@ -53,9 +55,34 @@
}
}
}
+
+ function hide_convert_answer_links(post_id){
+ var answer_convert_id = 'post-' + post_id + '-convert';
+ var convert_answer = document.getElementById(answer_convert_id);
+ if (data['userIsAdminOrMod']){
+ var answer_id = 'post-id-' + post_id;
+ var answer_container = document.getElementById(answer_id);
+ var answer_element= answer_container.getElementsByClassName('answer-body')[0].children[1];
+ if (answer_element.textContent.length > 300){
+ convert_answer.parentNode.removeChild(convert_answer);
+ }
+ } else{
+ convert_answer.parentNode.removeChild(convert_answer);
+ }
+ }
+
+ function hidePublishAnswerLink(postId) {
+ if (data['userIsThreadModerator'] === false) {
+ //hide publish/unpublish answer links
+ var answerId = 'post-' + postId + '-publish';
+ var pubBtn = document.getElementById(answerId);
+ pubBtn.parentNode.removeChild(pubBtn);
+ }
+ }
+
function render_post_controls(post_id){
if (data['userIsAdminOrMod']){
- return;//all functions on
+ return;//all remaining functions stay on
}
if (data['user_posts'] === undefined) {
return;
@@ -155,11 +182,24 @@
);
}
}
+
+ function hide_convert_links(){
+ if (!askbot['data']['userIsAdminOrMod']){
+ var links = document.getElementsByClassName('convert-comment');
+ for (i=0; i<links.length; i++){
+ links[i].setAttribute('style', 'display:none;');
+ }
+ }
+ }
+
askbot['functions'] = askbot['functions'] || {};
askbot['functions']['renderPostVoteButtons'] = render_vote_buttons;
askbot['functions']['renderPostControls'] = render_post_controls;
askbot['functions']['renderAddCommentButton'] = render_add_comment_button;
askbot['functions']['renderAddAnswerButton'] = render_add_answer_button;
+ askbot['functions']['hideConvertLinks'] = hide_convert_links;
+ askbot['functions']['hideConvertAnswerLinks'] = hide_convert_answer_links;
+ askbot['functions']['hidePublishAnswerLink'] = hidePublishAnswerLink;
})();
/*]]>*/
</script>
diff --git a/askbot/skins/default/templates/question/answer_card.html b/askbot/skins/default/templates/question/answer_card.html
index 7161c186..ae7d30fa 100644
--- a/askbot/skins/default/templates/question/answer_card.html
+++ b/askbot/skins/default/templates/question/answer_card.html
@@ -16,6 +16,9 @@
<div class="post-update-info-container">
{% include "question/answer_author_info.html" %}
</div>
+ {% if answer.id in published_answer_ids %}
+ <p><strong>{% trans %}This response is published{% endtrans %}</strong></p>
+ {% endif %}
{{ answer.html }}
</div>
<div class="answer-controls post-controls">
diff --git a/askbot/skins/default/templates/question/javascript.html b/askbot/skins/default/templates/question/javascript.html
index 4399c823..5dca2522 100644
--- a/askbot/skins/default/templates/question/javascript.html
+++ b/askbot/skins/default/templates/question/javascript.html
@@ -9,6 +9,8 @@
askbot['urls']['postComments'] = '{% url post_comments %}';
askbot['urls']['editComment'] = '{% url edit_comment %}';
askbot['urls']['deleteComment'] = '{% url delete_comment %}';
+ askbot['urls']['convertComment'] = '{% url comment_to_answer %}';
+ askbot['urls']['convertAnswer'] = '{% url answer_to_comment %}';
askbot['urls']['getComment'] = '{% url get_comment %}';
askbot['urls']['saveDraftAnswer'] = '{% url save_draft_answer %}';
askbot['urls']['question_url_template'] = scriptUrl + '{{ 'question/'|transurl }}{{ "{{QuestionID}}/{{questionSlug}}" }}';{# yes it needs to be that whacky #}
@@ -19,6 +21,8 @@
askbot['urls']['delete_post'] = '{% url delete_post %}';
askbot['urls']['get_html_template'] = '{% url get_html_template %}';
askbot['urls']['getGroupsList'] = '{% url get_groups_list %}';
+ askbot['urls']['publishAnswer'] = '{% url publish_answer %}';
+ askbot['data']['userIsThreadModerator'] = {% if user_is_thread_moderator %}true{% else %}false{% endif %};
askbot['messages']['addComment'] = '{% trans %}post a comment{% endtrans %}';
{% if settings.SAVE_COMMENT_ON_ENTER %}
askbot['settings']['saveCommentOnEnter'] = true;
@@ -27,11 +31,10 @@
{% endif %}
askbot['settings']['tagSource'] = '{{ settings.TAG_SOURCE }}';
</script>
+<script type="text/javascript" src='{{"/bootstrap/js/bootstrap.js"|media}}'></script>
{% if settings.EDITOR_TYPE == 'markdown' %}
<script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/wmd/wmd.js"|media}}'></script>
-{% else %}
- {% include "meta/tinymce.html" %}
{% endif %}
<script type='text/javascript' src='{{"/js/jquery.validate.min.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/post.js"|media}}'></script>
diff --git a/askbot/skins/default/templates/question/new_answer_form.html b/askbot/skins/default/templates/question/new_answer_form.html
index 260ffb95..76772abf 100644
--- a/askbot/skins/default/templates/question/new_answer_form.html
+++ b/askbot/skins/default/templates/question/new_answer_form.html
@@ -1,6 +1,5 @@
<form
id="fmanswer"
- {#% if user == question.author %}style="display:none"{% endif %#}
action="{% url answer question.id %}"
method="post"
>{% csrf_token %}
diff --git a/askbot/skins/default/templates/question/sidebar.html b/askbot/skins/default/templates/question/sidebar.html
index ef99e988..4d431ef2 100644
--- a/askbot/skins/default/templates/question/sidebar.html
+++ b/askbot/skins/default/templates/question/sidebar.html
@@ -49,38 +49,24 @@
</div>
<div class="clearfix"></div>
+{% if settings.GROUPS_ENABLED %}
<div class="box sharing-widget">
{% if thread.is_private() %}
- <h2>{% trans %}Share{% endtrans %}</h2>
- <label for="share_user_name">{% trans %}Share with users{% endtrans %}</label>
- <p>{% trans %}Yourself{% endtrans %}</p>
- {% for group in thread.groups.all() %}
- {% if group.name.startswith('_internal_') %}
- <p>{{ group.created_by.get_profile_link() }}<p>
- {% endif %}
- {% endfor %}
+ <h2>{% trans %}Invite{% endtrans %}</h2>
+ <p style="margin: 16px 0"
+ >Invite others to help answer this question</p>
<form action="{% url share_question_with_user %}" method="post">{% csrf_token %}
<input id="share_user_name" type="text" class="groups-input" name="recipient_name" />
<input type="hidden" name="thread_id" value="{{ thread.id }}"/>
<input type="submit" class="add-groups" value="{% trans %}add{% endtrans %}"/>
</form>
- {#% if thread.groups.count() %}
- <label for="group_name">{% trans %}Shared with groups:{% endtrans %}</label>
- {% else %}
- <label for="group_name">{% trans %}Share with a group{% endtrans %}</label>
- {% endif %#}
- <label for="share_group_name">{% trans %}Share with groups{% endtrans %}</label>
- {% for group in thread.groups.all() %}
- {% if not group.name.startswith('_internal_') %}{# todo: fix this hack #}
- <p>{{ macros.user_group(group) }}</p>
- {% endif %}
- {% endfor %}
+ <p class="share-input-col">{% trans %}- or -{% endtrans %}</p>
<form action="{% url share_question_with_group %}" method="post">{% csrf_token %}
<input id="share_group_name" type="text" class="groups-input" name="recipient_name" />
<input type="hidden" name="thread_id" value="{{ thread.id }}"/>
<input type="submit" class="add-groups" value="{% trans %}add{% endtrans %}"/>
</form>
- <p style="text-align: center">{% trans %}or{% endtrans %}</p>
+ <p class="share-input-col">{% trans %}- or -{% endtrans %}</p>
<form action="{% url share_question_with_group %}" method="post">{% csrf_token %}
<input
type="hidden"
@@ -88,18 +74,71 @@
value="{{ settings.GLOBAL_GROUP_NAME }}"
/>
<input type="hidden" name="thread_id" value="{{ thread.id }}"/>
+ <p class="share-input-col">
<input
type="submit"
class="add-groups add-everyone-group"
value="{% trans %}share with everyone{% endtrans %}"
/>
+ </p>
</form>
+ {% set shared_users_count = sharing_info['users'].count() %}
+ {% set shared_groups_count = sharing_info['groups'].count() %}
+
+ {% if shared_users_count or shared_groups_count %}
+ <p
+ style="margin:16px 0 4px 0"
+ >{% trans %}This question is currently shared only with:{% endtrans %}</p>
+ {% endif %}
+ <h3>{% trans %}Individual users{% endtrans %}</h3>
+ {% set comma = joiner(',') %}
+ {{ comma() }}
+ <p>
+ <a href="{{ request.user.get_profile_url() }}">
+ {% trans %}You{% endtrans -%}
+ </a>{%- if shared_users_count -%}
+ {%- for user in sharing_info['users'] %}{{ comma() }}
+ {{ user.get_profile_link() }}
+ {%- endfor -%}
+ {% endif -%}
+ {%- if sharing_info['more_users_count'] > 0 %}
+ {% trans %}and{% endtrans %}
+ <a
+ class="see-related-users"
+ data-url="{% url get_thread_shared_users %}"
+ data-thread-id="{{ thread.id }}"
+ >{% trans
+ more_count=sharing_info['more_users_count']
+ %}{{ more_count }} more{% endtrans %}
+ </a>
+ {% endif %}
+ </p>
+
+ {% if shared_groups_count %}
+ <h3>{% trans %}Groups{% endtrans %}</h3>
+ <p>
+ {% set comma = joiner(',') %}
+ {%- for group in sharing_info['groups'] -%}{{ comma() }}
+ {{ macros.user_group_link(group) }}
+ {%- endfor -%}
+ {% if sharing_info['more_groups_count'] > 0 %}
+ {% trans %}and{% endtrans %}
+ <a
+ class="see-related-groups"
+ data-url="{% url get_thread_shared_groups %}"
+ data-thread-id="{{ thread.id }}"
+ >{% trans more_count=sharing_info['more_groups_count'] %}{{ more_count }} more{% endtrans %}
+ </a>
+ {% endif %}
+ </p>
+ {% endif %}
{% else %}
<h2>{% trans %}Public thread{% endtrans %}</h2>
<p>{% trans site_name=settings.APP_SHORT_NAME %}This thread is public, all members of {{ site_name }} can read this page.{% endtrans %}</p>
{% endif %}
</div>
+{% endif %}
{% if settings.SIDEBAR_QUESTION_SHOW_META %}
<div class="box statsWidget">
@@ -117,7 +156,7 @@
</div>
{% endif %}
-{% if similar_threads.data and settings.SIDEBAR_QUESTION_SHOW_RELATED %}
+{% if similar_threads.data() and settings.SIDEBAR_QUESTION_SHOW_RELATED %}
{#% cache 1800 "related_questions" related_questions question.id language_code %#}
<div class="box">
<h2>{% trans %}Related questions{% endtrans %}</h2>
diff --git a/askbot/skins/default/templates/question_edit.html b/askbot/skins/default/templates/question_edit.html
index f4e4fbbf..f176b11d 100644
--- a/askbot/skins/default/templates/question_edit.html
+++ b/askbot/skins/default/templates/question_edit.html
@@ -64,8 +64,6 @@
{% if settings.EDITOR_TYPE == 'markdown' %}
<script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script>
<script type='text/javascript' src='{{"/js/wmd/wmd.js"|media}}'></script>
- {% else %}
- {% include "meta/tinymce.html" %}
{% endif %}
<script type="text/javascript">
{% if settings.ENABLE_MATHJAX or settings.MARKUP_CODE_FRIENDLY %}
diff --git a/askbot/skins/default/templates/user_inbox/base.html b/askbot/skins/default/templates/user_inbox/base.html
new file mode 100644
index 00000000..4d74f8bc
--- /dev/null
+++ b/askbot/skins/default/templates/user_inbox/base.html
@@ -0,0 +1,63 @@
+{% extends "user_profile/user.html" %}
+{% block before_css %}
+ <link href="{{'/bootstrap/css/bootstrap.css'|media}}" rel="stylesheet" type="text/css" />
+{% endblock %}
+{% block profilesection %}
+ {% trans %}inbox{% endtrans %}
+{% endblock %}
+{% block usercontent %}
+<div style="padding-top:5px;font-size:13px;">
+ {% set re_count = request.user.new_response_count +
+ request.user.seen_response_count
+ %}
+ {% if re_count + flags_count + group_join_requests_count > 0 %}
+ <div id="re_sections">
+ {% trans %}Sections:{% endtrans %}
+ {% set sep = joiner('|') %}
+ {% if re_count > 0 %}{{ sep() }}
+ <a href="{{request.user.get_absolute_url()}}?sort=inbox&section=forum"
+ {% if inbox_section == 'forum' %}class="on"{% endif %}
+ >
+ {% trans %}forum responses ({{re_count}}){% endtrans -%}
+ </a>
+ {% endif %}
+ {% if flags_count > 0 %}{{ sep() }}
+ <a href="{{request.user.get_absolute_url()}}?sort=inbox&section=flags"
+ {% if inbox_section == 'flags' %}class="on"{% endif %}
+ >
+ {% trans %}flagged items ({{flags_count}}){% endtrans %}
+ </a>
+ {% endif %}
+ {% if group_join_requests_count %}{{ sep() }}
+ <a href="{{request.user.get_absolute_url()}}?sort=inbox&section=join_requests"
+ {% if inbox_section == 'join_requests' %}class="on"{% endif %}
+ >
+ {% trans %}group join requests{% endtrans %}
+ </a>
+ {% endif %}
+ </div>
+ {% endif %}
+ {% block inbox_content %}
+ {% endblock %}
+</div>
+{% endblock %}
+{% block userjs %}
+ <script type="text/javascript">
+ 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 %}';
+ {% if request.user.is_administrator_or_moderator() %}
+ askbot['data']['postRejectReasons'] = [
+ {% for reason in post_reject_reasons %}
+ {'id': {{reason.id}}, 'title': '{{reason.title|escapejs}}'},
+ {% endfor %}
+ ];
+ {% endif %}
+ $(document).ready(function(){
+ $('body').addClass('inbox-{{ inbox_section }}');
+ setup_inbox();
+ });
+ </script>
+{% endblock %}
diff --git a/askbot/skins/default/templates/user_inbox/group_join_requests.html b/askbot/skins/default/templates/user_inbox/group_join_requests.html
new file mode 100644
index 00000000..2defe5e1
--- /dev/null
+++ b/askbot/skins/default/templates/user_inbox/group_join_requests.html
@@ -0,0 +1,50 @@
+{% extends "user_inbox/base.html" %}
+{% import "macros.html" as macros %}
+{% block profilesection %}
+ {% trans %}inbox - group join requests{% endtrans %}
+{% endblock %}
+{% block inbox_content %}
+ <table>
+ {% for join_request in join_requests %}
+ <tr>
+ <td>{% trans
+ user=join_request.user,
+ group=groups_dict[join_request.object_id].name
+ %}{{ user }} wants to join group {{ group }}{% endtrans %}
+ </td>
+ <td>{# forms with accept and reject buttons #}
+ <form action="{% url moderate_group_join_request %}" method="post"
+ >{% csrf_token %}
+ <input
+ type="hidden"
+ name="request_id"
+ value="{{join_request.id}}"
+ />
+ <input type="hidden" name="action" value="approve"/>
+ <input
+ class="btn"
+ type="submit"
+ value="{% trans %}Approve{% endtrans %}"
+ />
+ </form>
+ </td>
+ <td>
+ <form action="{% url moderate_group_join_request %}" method="post">
+ {% csrf_token %}
+ <input
+ type="hidden"
+ name="request_id"
+ value="{{join_request.id}}"
+ />
+ <input type="hidden" name="action" value="deny"/>
+ <input
+ class="btn"
+ type="submit"
+ value="{% trans %}Deny{% endtrans %}"
+ />
+ </form>
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+{% endblock %}
diff --git a/askbot/skins/default/templates/user_inbox/responses_and_flags.html b/askbot/skins/default/templates/user_inbox/responses_and_flags.html
new file mode 100644
index 00000000..c889bb0a
--- /dev/null
+++ b/askbot/skins/default/templates/user_inbox/responses_and_flags.html
@@ -0,0 +1,43 @@
+{% extends "user_inbox/base.html" %}
+{% import "macros.html" as macros %}
+{% block profilesection %}
+ {% trans %}inbox - responses{% endtrans %}
+{% endblock %}
+{% block inbox_content %}
+ <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 />
+ <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>
+ {% include "user_profile/reject_post_dialog.html" %}
+ <div id="responses">
+ {% for response in responses %}
+ <div class="response-parent" data-response-id="{{response.id}}">
+ <p class="headline">
+ <strong>"{{ response.response_title.strip()|escape}}"</strong>
+ </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>
+ <div class="clearfix"></div>
+ {% endfor %}
+ </div>
+ </div>
+{% endblock %}
diff --git a/askbot/skins/default/templates/user_profile/user_inbox.html b/askbot/skins/default/templates/user_profile/user_inbox.html
deleted file mode 100644
index 9f3461f1..00000000
--- a/askbot/skins/default/templates/user_profile/user_inbox.html
+++ /dev/null
@@ -1,107 +0,0 @@
-{% 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
-they are a generalized form of any response and
-
-The following properties of response object are used:
-timestamp - when it happened
-user - user who gave response (database object)
-response_type - type of response
-response_url - link to the question
-response_title - title of the question
-response_snippet - abbreviated content of the response
-inbox_section - forum|flags
-#}
-{% block profilesection %}
- {% trans %}inbox{% endtrans %}
-{% endblock %}
-{% block usercontent %}
- <div style="padding-top:5px;font-size:13px;">
- {% set re_count = request.user.new_response_count +
- request.user.seen_response_count
- %}
- {% if moderation_items %}
- {% set flag_count = moderation_items['new_count'] +
- moderation_items['seen_count']
- %}
- {% else %}
- {% set flag_count = 0 %}
- {% endif %}
- {% if re_count > 0 and flag_count > 0 %}
- <div id="re_sections">
- {% trans %}Sections:{% endtrans %}
- <a href="{{request.user.get_absolute_url()}}?sort=inbox&section=forum"
- {% if inbox_section == 'forum' %}class="on"{% endif %}
- >
- {% trans %}forum responses ({{re_count}}){% endtrans -%}
- </a> |
- <a href="{{request.user.get_absolute_url()}}?sort=inbox&section=flags"
- {% if inbox_section == 'flags' %}class="on"{% endif %}
- >
- {% trans %}flagged items ({{flag_count}}){% endtrans %}
- </a>
- </div>
- {% endif %}
- <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 />
- <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>
- {% include "user_profile/reject_post_dialog.html" %}
- <div id="responses">
- {% for response in responses %}
- <div class="response-parent" data-response-id="{{response.id}}">
- <p class="headline">
- <strong>"{{ response.response_title.strip()|escape}}"</strong>
- </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>
- <div class="clearfix"></div>
- {% endfor %}
- </div>
- </div>
-{% endblock %}
-{% block userjs %}
- <script type="text/javascript">
- 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 %}';
- {% if request.user.is_administrator_or_moderator() %}
- askbot['data']['postRejectReasons'] = [
- {% for reason in post_reject_reasons %}
- {'id': {{reason.id}}, 'title': '{{reason.title|escapejs}}'},
- {% endfor %}
- ];
- {% endif %}
- $(document).ready(function(){
- $('body').addClass('inbox-{{ inbox_section }}');
- setup_inbox();
- });
- </script>
-<!-- end user_responses.html -->
-{% endblock %}
diff --git a/askbot/skins/default/templates/users.html b/askbot/skins/default/templates/users.html
index 183381d4..a1c4ed11 100644
--- a/askbot/skins/default/templates/users.html
+++ b/askbot/skins/default/templates/users.html
@@ -16,7 +16,7 @@
</h1>
<div class="tabBar">
<div class="tabsA">
- {% if settings.GROUPS_ENABLED and user_groups %}
+ {#% if settings.GROUPS_ENABLED and user_groups %}
<span class="label">{% trans %}Select/Sort by &raquo;{% endtrans %}</span>
{% for a_group in user_groups %}
<a
@@ -25,9 +25,9 @@
title="{% trans name=a_group.name|escape %}people in group {{name}}{% endtrans %}"
><span>{{ a_group.name|replace('-',' ')|escape }}</span></a>
{% endfor %}
- {% else %}
+ {% else %#}
<span class="label">{% trans %}Sort by &raquo;{% endtrans %}</span>
- {% endif %}
+ {#% endif %#}
{% if settings.KARMA_MODE == 'public' %}
<a
id="sort_reputation"
@@ -84,8 +84,8 @@
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']['load_object_description'] = '{% url load_object_description %}';
+ askbot['urls']['save_object_description'] = '{% url save_object_description %}';
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 %}';
diff --git a/askbot/skins/default/templates/widget_base.html b/askbot/skins/default/templates/widget_base.html
index fd5e739e..44be3e5f 100644
--- a/askbot/skins/default/templates/widget_base.html
+++ b/askbot/skins/default/templates/widget_base.html
@@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
{% spaceless %}
<head>
- {% include "meta/bottom_scripts.html" %}
+ {% include "meta/html_head_javascript.html" %}
{% block before_css %}{% endblock %}
{% block forestyle %}{% endblock %}
{% block forejs %}{% endblock %}
@@ -14,6 +14,7 @@
{% endblock %}
{% block content%}
{% endblock %}
+ {% include "meta/bottom_scripts.html" %}
{% block endjs %}
{% endblock %}
</body>
diff --git a/askbot/skins/default/templates/widgets/group_info.html b/askbot/skins/default/templates/widgets/group_info.html
index 5d3a4c7f..cba8177a 100644
--- a/askbot/skins/default/templates/widgets/group_info.html
+++ b/askbot/skins/default/templates/widgets/group_info.html
@@ -2,29 +2,29 @@
<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 }}"
+ {% if group.logo_url %}
+ src="{{ group.logo_url }}"
{% else %}
style="display:none"
{% endif %}
/>
<div class="content">
- {% if group.tag_wiki %}
- {{ group.tag_wiki.html }}
+ {% if group.description %}
+ {{ group.description.html }}
{% endif %}
</div>
<div class="clearfix"></div>
{{ macros.group_join_button(
group_id = group.id,
- can_join = user_can_join_group,
- is_member = user_is_group_member
+ acceptance_level = user_acceptance_level,
+ membership_level = user_membership_level
)
}}
{% 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 %}
+ {% if group.logo_url %}
<span>|</span>
<a class="change-logo"
>{% trans %}change logo{% endtrans %}</a>
@@ -39,7 +39,7 @@
{% if group_email_moderation_enabled %}
<input type="checkbox"
id="moderate-email"
- {% if group.group_profile.moderate_email %}checked="checked"{% endif %}
+ {% if group.moderate_email %}checked="checked"{% endif %}
data-toggle-url="{% url toggle_group_profile_property %}"
/>
<label for="moderate-email">
@@ -47,20 +47,41 @@
</label>
<br/>
{% endif %}
- <input type="checkbox"
- id="open-or-close-group"
- {% if group.group_profile.is_open %}checked="checked"{% endif %}
+
+ <input
+ type="checkbox"
+ id="moderate-answers-to-enquirers"
+ {% if group.moderate_answers_to_enquirers %}checked="checked"{% endif %}
data-toggle-url="{% url toggle_group_profile_property %}"
/>
- <label for="open-or-close-group">
- {% trans %}anyone can join{% endtrans %}
+ <label for="moderate-answers-to-enquirers">
+ {% trans %}show only selected answers to enquirers{% endtrans %}
</label>
<br/>
+
+ <label for="group-openness-selector">
+ {% trans %}How users join this group?{% endtrans %}
+ </label>
+ <br/>
+ <select
+ id="group-openness-selector"
+ data-url="{% url set_group_openness %}"
+ >
+ {% for choice in group_openness_choices %}
+ <option
+ {% if choice[0] == group.openness %}
+ selected="selected"
+ {% endif %}
+ value="{{choice[0]}}"
+ >{{ choice[1] }}</option>
+ {% endfor %}
+ </select>
+ <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-object-id="{{group.group_ptr_id}}"
+ data-model-name="Group"
data-property-name="preapproved_emails"
data-url="{% url edit_object_property_text %}"
data-editor-heading="{% trans %}List of preapproved email addresses{% endtrans %}"
@@ -70,8 +91,8 @@
<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-object-id="{{group.group_ptr_id}}"
+ data-model-name="Group"
data-property-name="preapproved_email_domains"
data-url="{% url edit_object_property_text %}"
data-editor-heading="{% trans %}List of preapproved email domain names{% endtrans %}"
diff --git a/askbot/skins/default/templates/widgets/groups_list.html b/askbot/skins/default/templates/widgets/groups_list.html
new file mode 100644
index 00000000..0669f34f
--- /dev/null
+++ b/askbot/skins/default/templates/widgets/groups_list.html
@@ -0,0 +1,4 @@
+{% import "macros.html" as macros %}
+{% for group in groups %}
+ <p>{{ macros.user_group(group) }}</p>
+{% endfor %}
diff --git a/askbot/skins/default/templates/widgets/meta_nav.html b/askbot/skins/default/templates/widgets/meta_nav.html
index 1b28c787..20c22491 100644
--- a/askbot/skins/default/templates/widgets/meta_nav.html
+++ b/askbot/skins/default/templates/widgets/meta_nav.html
@@ -1,20 +1,23 @@
+{% import "macros.html" as macros%}
<a
id="navTags"
href="{% url tags %}"
{% if active_tab == 'tags' %}class="on"{% endif %}
>{% trans %}tags{% endtrans %}</a>
+{% if settings.GROUPS_ENABLED %}
+<span class="dropdown">
+<a
+ id="navGroups" class='{% if active_tab == 'groups' %}"on"{% endif %}'
+ href="{% url groups %}" data-target="#" >
+ {% trans %}people & groups{% endtrans %}
+</a>
+</span>
+{%else%}
<a
id="navUsers"
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
diff --git a/askbot/skins/default/templates/widgets/user_list.html b/askbot/skins/default/templates/widgets/user_list.html
index 2e78bd0c..52cf8bd4 100644
--- a/askbot/skins/default/templates/widgets/user_list.html
+++ b/askbot/skins/default/templates/widgets/user_list.html
@@ -1,4 +1,4 @@
-{% from "macros.html" import gravatar %}
+{% import "macros.html" as macros %}
<div class="userList">
<table class="list-table">
<tr>
@@ -6,10 +6,10 @@
{% for user in users %}
<div class="user">
<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|escape}}</a>{{ user_country_flag(user) }}</li>
+ <li class="thumb">{{ macros.gravatar(user, 32) }}</li>
+ <li><a href="{% url user_profile user.id, user.username|slugify %}{% if profile_section %}?sort={{profile_section}}{% endif %}">{{user.username|escape}}</a>{{ macros.user_country_flag(user) }}</li>
<li>{{
- user_score_and_badge_summary(
+ macros.user_score_and_badge_summary(
user,
karma_mode = karma_mode,
badges_mode = badges_mode
diff --git a/askbot/skins/default/templates/widgets/user_navigation.html b/askbot/skins/default/templates/widgets/user_navigation.html
index 717cd7ee..06b0cdb9 100644
--- a/askbot/skins/default/templates/widgets/user_navigation.html
+++ b/askbot/skins/default/templates/widgets/user_navigation.html
@@ -17,9 +17,10 @@
<a href="{{ settings.LOGOUT_URL }}?next={{ settings.LOGOUT_REDIRECT_URL }}">{% trans %}sign out{% endtrans %}</a>
{% endif %}
{% elif settings.USE_ASKBOT_LOGIN_SYSTEM %}
- <a href="{{ settings.LOGIN_URL }}?next={{request.path|clean_login_url}}">{% trans %}Hi, there! Please sign in{% endtrans %}</a>
+ <a href="{{ settings.LOGIN_URL }}?next={{request.path|clean_login_url}}">{% trans %}Hi there! Please sign in{% endtrans %}</a>
{% endif %}
{% if request.user.is_authenticated() and request.user.is_administrator() %}
<a href="{% url site_settings %}">{% trans %}settings{% endtrans %}</a>
+ <a href="{% url widgets %}">{% trans %}widgets{% endtrans %}</a>
{% endif %}
<a href="{% url "help" %}" title="{% trans %}help{% endtrans %}">{% trans %}help{% endtrans %}</a>
diff --git a/askbot/skins/loaders.py b/askbot/skins/loaders.py
index aa3188e9..c1367fe5 100644
--- a/askbot/skins/loaders.py
+++ b/askbot/skins/loaders.py
@@ -115,14 +115,20 @@ def get_template(template, request = None):
skin.set_language(request.LANGUAGE_CODE)
return skin.get_template(template)
+def render_into_skin_as_string(template, data, request):
+ context = RequestContext(request, data)
+ template = get_template(template, request)
+ return template.render(context)
+
def render_into_skin(template, data, request, mimetype = 'text/html'):
"""in the future this function will be able to
switch skin depending on the site administrator/user selection
right now only admins can switch
"""
- context = RequestContext(request, data)
- template = get_template(template, request)
- return HttpResponse(template.render(context), mimetype = mimetype)
+ return HttpResponse(
+ render_into_skin_as_string(template, data, request),
+ mimetype=mimetype
+ )
def render_text_into_skin(text, data, request):
context = RequestContext(request, data)
diff --git a/askbot/startup_procedures.py b/askbot/startup_procedures.py
index 0fec6d5f..a0c60800 100644
--- a/askbot/startup_procedures.py
+++ b/askbot/startup_procedures.py
@@ -18,6 +18,7 @@ from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured
from askbot.utils.loading import load_module
from askbot.utils.functions import enumerate_string_list
+from askbot.utils.url_utils import urls_equal
from urlparse import urlparse
PREAMBLE = """\n
@@ -512,6 +513,84 @@ def test_custom_user_profile_tab():
footer = 'Please carefully read about adding a custom user profile tab.'
print_errors(errors, header = header, footer = footer)
+def get_tinymce_sample_config():
+ """returns the sample configuration for TinyMCE
+ as string"""
+ askbot_root = askbot.get_install_directory()
+ file_path = os.path.join(
+ askbot_root, 'setup_templates', 'tinymce_sample_config.py'
+ )
+ config_file = open(file_path, 'r')
+ sample_config = config_file.read()
+ config_file.close()
+ return sample_config
+
+def test_tinymce():
+ """tests the tinymce editor setup"""
+ errors = list()
+ if 'tinymce' not in django_settings.INSTALLED_APPS:
+ errors.append("add 'tinymce', to the INSTALLED_APPS")
+
+ required_attrs = (
+ 'TINYMCE_COMPRESSOR',
+ 'TINYMCE_JS_ROOT',
+ 'TINYMCE_URL',
+ 'TINYMCE_DEFAULT_CONFIG'
+ )
+
+ missing_attrs = list()
+ for attr in required_attrs:
+ if not hasattr(django_settings, attr):
+ missing_attrs.append(attr)
+
+ if missing_attrs:
+ errors.append('add missing settings: %s' % ', '.join(missing_attrs))
+
+ #check compressor setting
+ compressor_on = getattr(django_settings, 'TINYMCE_COMPRESSOR', False)
+ if compressor_on is False:
+ errors.append('add line: TINYMCE_COMPRESSOR = True')
+
+ #check js root setting
+ js_root = getattr(django_settings, 'TINYMCE_JS_ROOT', '')
+ relative_js_path = 'common/media/js/tinymce/'
+ expected_js_root = os.path.join(django_settings.STATIC_ROOT, relative_js_path)
+ if os.path.normpath(js_root) != os.path.normpath(expected_js_root):
+ js_root_template = "add line: TINYMCE_JS_ROOT = os.path.join(STATIC_ROOT, '%s')"
+ errors.append(js_root_template % relative_js_path)
+
+ #check url setting
+ url = getattr(django_settings, 'TINYMCE_URL', '')
+ expected_url = django_settings.STATIC_URL + relative_js_path
+ if urls_equal(url, expected_url) is False:
+ js_url_template = "add line: TINYMCE_URL = STATIC_URL + '%s'"
+ errors.append(js_url_template % relative_js_path)
+
+ if errors:
+ header = 'Please add the tynymce editor configuration ' + \
+ 'to your settings.py file.'
+ footer = 'You might want to use this sample configuration ' + \
+ 'as template:\n\n' + get_tinymce_sample_config()
+ print_errors(errors, header=header, footer=footer)
+
+def test_longerusername():
+ """tests proper installation of the "longerusername" app
+ """
+ errors = list()
+ if 'longerusername' not in django_settings.INSTALLED_APPS:
+ errors.append(
+ "add 'longerusername', as the first item in the INSTALLED_APPS"
+ )
+ else:
+ index = django_settings.INSTALLED_APPS.index('longerusername')
+ if index != 0:
+ message = "move 'longerusername', to the beginning of INSTALLED_APPS"
+ raise AskbotConfigError(message)
+
+ if errors:
+ errors.append('run "python manage.py migrate longerusername"')
+ print_errors(errors)
+
def run_startup_tests():
"""function that runs
all startup tests, mainly checking settings config so far
@@ -526,7 +605,9 @@ def run_startup_tests():
test_middleware()
test_celery()
#test_csrf_cookie_domain()
+ test_tinymce()
test_staticfiles()
+ test_longerusername()
test_avatar()
settings_tester = SettingsTester({
'CACHE_MIDDLEWARE_ANONYMOUS_ONLY': {
diff --git a/askbot/tests/__init__.py b/askbot/tests/__init__.py
index 056320f5..4cecf930 100644
--- a/askbot/tests/__init__.py
+++ b/askbot/tests/__init__.py
@@ -18,3 +18,5 @@ from askbot.tests.reply_by_email_tests import *
from askbot.tests.widget_tests import *
from askbot.tests.category_tree_tests import CategoryTreeTests
from askbot.tests.user_model_tests import UserModelTests
+from askbot.tests.utils_tests import *
+from askbot.tests.view_context_tests import *
diff --git a/askbot/tests/badge_tests.py b/askbot/tests/badge_tests.py
index b66eadcc..0ed4b343 100644
--- a/askbot/tests/badge_tests.py
+++ b/askbot/tests/badge_tests.py
@@ -318,7 +318,7 @@ class BadgeTests(AskbotTestCase):
def test_guru_badge1(self):
self.assert_guru_badge_works('upvote_answer')
- def test_guru_badge1(self):
+ def test_guru_badge2(self):
self.assert_guru_badge_works('accept_best_answer')
def test_necromancer_badge(self):
diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py
index 07590487..fa166ba7 100644
--- a/askbot/tests/db_api_tests.py
+++ b/askbot/tests/db_api_tests.py
@@ -1,4 +1,4 @@
-"""Tests database api - the basic data entry
+"""Tests database api - the basic data entry
functions that happen on behalf of users
e.g. ``some_user.do_something(...)``
@@ -249,7 +249,7 @@ class UserLikeTagTests(AskbotTestCase):
self.setup_wildcard('aouaou* o* on* oeu*', 'bad')
self.assert_affinity_is('like', False)
self.assert_affinity_is('dislike', True)
-
+
self.setup_wildcard('one*', 'good')
self.assert_affinity_is('like', True)
self.assert_affinity_is('dislike', False)
@@ -297,7 +297,7 @@ class GlobalTagSubscriberGetterTests(AskbotTestCase):
self.assertEquals(actual_subscribers, expected_subscribers)
def test_nobody_likes_any_tags(self):
- """no-one had marked tags, so the set
+ """no-one had marked tags, so the set
of subscribers must be empty
"""
self.assert_subscribers_are(
@@ -401,7 +401,7 @@ class CommentTests(AskbotTestCase):
comment = models.Post.objects.get_comments().get(id = self.comment.id)
self.assertEquals(comment.score, 0)
-class TagAndGroupTests(AskbotTestCase):
+class GroupTests(AskbotTestCase):
def setUp(self):
self.u1 = self.create_user('u1')
askbot_settings.update('GROUPS_ENABLED', True)
@@ -430,13 +430,6 @@ class TagAndGroupTests(AskbotTestCase):
'question_comment': question_comment,
'answer_comment': answer_comment
}
-
- def test_group_cannot_create_case_variant_tag(self):
- self.post_question(user = self.u1, tags = 'one two three')
- models.Tag.group_tags.get_or_create(user = self.u1, group_name = 'One')
- tag_one = models.Tag.objects.filter(name__iexact = 'one')
- self.assertEqual(tag_one.count(), 1)
- self.assertEqual(tag_one[0].name, 'one')
def test_posts_added_to_global_group(self):
q = self.post_question(user=self.u1)
@@ -450,7 +443,7 @@ class TagAndGroupTests(AskbotTestCase):
self.assertEqual(c.groups.filter(name=group_name).exists(), True)
def test_posts_added_to_private_group(self):
- group = self.create_group(group_name='private', user=self.u1)
+ group = self.create_group(group_name='private')
self.u1.join_group(group)
q = self.post_question(user=self.u1, is_private=True)
@@ -483,7 +476,7 @@ class TagAndGroupTests(AskbotTestCase):
def test_making_public_question_private_works(self):
question = self.post_question(user=self.u1)
comment = self.post_comment(parent_post=question, user=self.u1)
- group = self.create_group(group_name='private', user=self.u1)
+ group = self.create_group(group_name='private')
self.u1.join_group(group)
self.edit_question(question=question, user=self.u1, is_private=True)
self.assertEqual(question.groups.count(), 2)
@@ -496,20 +489,28 @@ class TagAndGroupTests(AskbotTestCase):
question = self.post_question(user=self.u1)
answer = self.post_answer(question=question, user=self.u1)
comment = self.post_comment(parent_post=answer, user=self.u1)
- group = self.create_group(group_name='private', user=self.u1)
+ group = self.create_group(group_name='private')
self.u1.join_group(group)
+
+ #membership in `group` should not affect things,
+ #because answer groups always inherit thread groups
self.edit_answer(user=self.u1, answer=answer, is_private=True)
- self.assertEqual(answer.groups.count(), 2)
- self.assertEqual(answer.groups.filter(id=group.id).count(), 1)
+ self.assertEqual(answer.groups.count(), 1)
+
+ #here we have a simple case - the comment to answer was posted
+ #by the answer author!!!
+ #won't work when comment was by someone else
+ u1_group = self.u1.get_personal_group()
+ self.assertEqual(answer.groups.filter(id=u1_group.id).count(), 1)
#comment inherits the sharing scope
- self.assertEqual(comment.groups.count(), 2)
- self.assertEqual(comment.groups.filter(id=group.id).count(), 1)
+ self.assertEqual(comment.groups.count(), 1)
+ self.assertEqual(comment.groups.filter(id=u1_group.id).count(), 1)
def test_public_question_private_answer_works(self):
question = self.post_question(self.u1)
u2 = self.create_user('u2')
- group = self.create_group(group_name='private', user=u2)
+ group = self.create_group(group_name='private')
u2.join_group(group)
answer = self.post_answer(question=question, user=u2, is_private=True)
@@ -528,7 +529,7 @@ class TagAndGroupTests(AskbotTestCase):
def test_thread_answer_count_for_multiple_groups(self):
question = self.post_question(self.u1)
- group = self.create_group(group_name='private', user=self.u1)
+ group = self.create_group(group_name='private')
self.u1.join_group(group)
answer = self.post_answer(question=question, user=self.u1)
answer.add_to_groups((group,))
@@ -536,7 +537,7 @@ class TagAndGroupTests(AskbotTestCase):
self.assertEqual(answer.thread.posts.get_answers(self.u1).count(), 1)
def test_thread_make_public_recursive(self):
- private_group = self.create_group(group_name='private', user=self.u1)
+ private_group = self.create_group(group_name='private')
self.u1.join_group(private_group)
data = self.post_question_answer_and_comments(is_private=True)
@@ -546,7 +547,7 @@ class TagAndGroupTests(AskbotTestCase):
self.assertObjectGroupsEqual(data['question_comment'], groups)
self.assertObjectGroupsEqual(data['answer'], groups)
self.assertObjectGroupsEqual(data['answer_comment'], groups)
-
+
data['thread'].make_public(recursive=True)
global_group = get_global_group()
@@ -560,7 +561,7 @@ class TagAndGroupTests(AskbotTestCase):
def test_thread_add_to_groups_recursive(self):
data = self.post_question_answer_and_comments()
- private_group = self.create_group(group_name='private', user=self.u1)
+ private_group = self.create_group(group_name='private')
thread = data['thread']
thread.add_to_groups([private_group], recursive=True)
@@ -573,9 +574,46 @@ class TagAndGroupTests(AskbotTestCase):
self.assertObjectGroupsEqual(data['answer_comment'], groups)
def test_private_thread_is_invisible_to_anonymous_user(self):
- group = self.create_group(group_name='private', user=self.u1)
+ group = self.create_group(group_name='private')
self.u1.join_group(group)
self.post_question(user=self.u1, is_private=True)
visible_threads = models.Thread.objects.get_visible(AnonymousUser())
self.assertEqual(visible_threads.count(), 0)
+
+ def test_join_group(self):
+ #create group
+ group = models.Group(name='somegroup')
+ group.openness = models.Group.OPEN
+ group.save()
+ #join
+ self.u1 = self.create_user('user1')
+ self.u1.join_group(group)
+ #assert membership of askbot group object
+ found_count = self.u1.get_groups().filter(name='somegroup').count()
+ self.assertEqual(found_count, 1)
+
+ def test_group_moderation(self):
+ #create group
+ group = models.Group(name='somegroup')
+ #make it moderated
+ group.openness = models.Group.MODERATED
+ group.save()
+
+ #add moderator to the group
+ mod = self.create_user('mod', status='d')
+ mod.join_group(group)
+
+ #create a regular user
+ reg = self.create_user('reg')
+ reg.join_group(group)
+ #assert that moderator has a notification
+ acts = models.Activity.objects.filter(
+ user=reg,
+ activity_type=const.TYPE_ACTIVITY_ASK_TO_JOIN_GROUP,
+ object_id=group.id
+ )
+ self.assertEqual(acts.count(), 1)
+ self.assertEqual(acts[0].recipients.count(), 1)
+ recipient = acts[0].recipients.all()[0]
+ self.assertEqual(recipient, mod)
diff --git a/askbot/tests/form_tests.py b/askbot/tests/form_tests.py
index be88cf39..90f4f4f2 100644
--- a/askbot/tests/form_tests.py
+++ b/askbot/tests/form_tests.py
@@ -374,11 +374,12 @@ class AskWidgetFormTests(AskbotTestCase):
self.bad_data = {'title': ''}
def test_valid_input(self):
- form_object = self.form(self.good_data)
+ form_object = self.form(include_text=False, data=self.good_data)
+ print form_object.errors
self.assertTrue(form_object.is_valid())
- form_object = self.form(self.good_data_anon)
+ form_object = self.form(include_text=False, data=self.good_data_anon)
self.assertTrue(form_object.is_valid())
def test_invalid_input(self):
- form_object = self.form(self.bad_data)
+ form_object = self.form(False, data=self.bad_data)
self.assertFalse(form_object.is_valid())
diff --git a/askbot/tests/misc_tests.py b/askbot/tests/misc_tests.py
new file mode 100644
index 00000000..328c213f
--- /dev/null
+++ b/askbot/tests/misc_tests.py
@@ -0,0 +1,91 @@
+from askbot.tests.utils import AskbotTestCase
+from askbot.models.post import PostRevision
+
+from django.test.client import Client
+from django.core.urlresolvers import reverse
+
+class MiscTests(AskbotTestCase):
+
+ def setUp(self):
+ self.u1 = self.create_user(username='user1')
+ self.u2 = self.create_user(username='user2')
+ self.u3 = self.create_user(username='user3')
+
+ def test_proper_PostRevision_manager_is_used(self):
+ "Makes sure that both normal and related managers for PostRevision don't implement .create() method"
+ question = self.post_question(user=self.u1)
+ self.assertRaises(NotImplementedError, question.revisions.create)
+ self.assertRaises(NotImplementedError, PostRevision.objects.create)
+
+class ContentConvertionTests(AskbotTestCase):
+
+ def setUp(self):
+ self.u1 = self.create_user(username='user1')
+ self.u1.set_password('password')
+ self.u1.set_admin_status()
+ self.u1.save()
+ self.u2 = self.create_user(username='notadmin')
+ self.client = Client()
+
+ #content
+ self.question = self.post_question(user=self.u1)
+ self.answer_to_convert = self.post_answer(user=self.u2,
+ question=self.question)
+ self.comment_on_answer = self.post_comment(user=self.u1,
+ parent_post=self.answer_to_convert)
+ self.another_answer = self.post_answer(user=self.u1,
+ question=self.question)
+ self.comment_to_convert = self.post_comment(user=self.u1,
+ parent_post=self.another_answer)
+
+ def test_convert_comment_to_answer(self):
+ self.client.login(username='user1', password='password')
+ old_parent_comment_count = self.another_answer.comment_count
+ answer_count = self.question.thread.answer_count
+ self.client.post(reverse('comment_to_answer'),
+ {'comment_id': self.comment_to_convert.id})
+ converted_answer = self.reload_object(self.comment_to_convert)
+ #old_parent = self.another_answer
+ old_parent = self.reload_object(self.another_answer)
+
+ #test for convertion
+ self.assertEquals(converted_answer.post_type, 'answer')
+ #test for parent change
+ self.assertNotEquals(old_parent.id, converted_answer.parent.id)
+ #test for answer count update
+ self.assertEquals(converted_answer.thread.answer_count, answer_count + 1)
+ #test for comment count update
+ self.assertEquals(old_parent.comment_count, old_parent_comment_count - 1)
+
+ #test the delete post view for errors
+ response = self.client.post(reverse('delete_post'),
+ {'post_id': converted_answer.id,
+ 'cancel_vote': 'false'},
+ HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ self.assertEquals(response.status_code, 200)
+ self.assertTrue('is_deleted' in response.content)
+
+ def test_convert_answer_to_comment(self):
+ comment_count = self.question.comment_count
+ #because the answer itself has a comment too!
+ comment_count += self.answer_to_convert.comment_count
+
+ answer_count = self.question.thread.answer_count
+ self.client.login(username='user1', password='password')
+ self.client.post(reverse('answer_to_comment'),
+ {'answer_id': self.answer_to_convert.id})
+ converted_comment = self.reload_object(self.answer_to_convert)
+ old_parent = self.reload_object(self.question)
+
+ #test for convertion
+ self.assertEquals(converted_comment.post_type, 'comment')
+ #test for answer count update
+ self.assertEquals(converted_comment.thread.answer_count, answer_count - 1)
+ #test for comment count update
+ self.assertEquals(old_parent.comment_count, comment_count + 1)
+
+ #test the delete comment view for errors
+ response = self.client.post(reverse('delete_comment'),
+ {'comment_id': converted_comment.id},
+ HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ self.assertEquals(response.status_code, 200)
diff --git a/askbot/tests/page_load_tests.py b/askbot/tests/page_load_tests.py
index e3e699a7..0f102975 100644
--- a/askbot/tests/page_load_tests.py
+++ b/askbot/tests/page_load_tests.py
@@ -5,6 +5,7 @@ from django.core.urlresolvers import reverse
from django.core import management
from django.core.cache.backends.dummy import DummyCache
from django.core import cache
+from django.utils import simplejson
import coffin
import coffin.template
@@ -15,6 +16,7 @@ from askbot.deployment import package_utils
from askbot.tests.utils import AskbotTestCase
from askbot.conf import settings as askbot_settings
from askbot.tests.utils import skipIf
+from askbot.tests.utils import with_settings
@@ -69,7 +71,7 @@ class PageLoadTestCase(AskbotTestCase):
self.old_cache = cache.cache
#Disable caching (to not interfere with production cache,
#not sure if that's possible but let's not risk it)
- cache.cache = DummyCache('', {})
+ cache.cache = DummyCache('', {})
def tearDown(self):
cache.cache = self.old_cache # Restore caching
@@ -149,6 +151,33 @@ class PageLoadTestCase(AskbotTestCase):
def test_ask_page_allowed_anonymous(self):
self.proto_test_ask_page(True, 200)
+ @with_settings({'GROUPS_ENABLED': False})
+ def test_api_get_questions_groups_disabled(self):
+ data = {'query': 'Question'}
+ response = self.client.get(reverse('api_get_questions'), data)
+ data = simplejson.loads(response.content)
+ self.assertTrue(len(data) > 1)
+
+ @with_settings({'GROUPS_ENABLED': True})
+ def test_api_get_questions_groups_enabled(self):
+
+ group = models.Group(name='secret group', openness=models.Group.OPEN)
+ group.save()
+ user = self.create_user('user')
+ user.join_group(group)
+ self.post_question(user=user, title='alibaba', group_id=group.id)
+
+ query_data = {'query': 'alibaba'}
+ response = self.client.get(reverse('api_get_questions'), query_data)
+ response_data = simplejson.loads(response.content)
+ self.assertEqual(len(response_data), 0)
+
+ self.client.login(method='force', user_id=user.id)
+ response = self.client.get(reverse('api_get_questions'), query_data)
+ response_data = simplejson.loads(response.content)
+ self.assertEqual(len(response_data), 1)
+
+
def test_ask_page_disallowed_anonymous(self):
self.proto_test_ask_page(False, 302)
@@ -159,6 +188,10 @@ class PageLoadTestCase(AskbotTestCase):
self.try_url('sitemap')
self.try_url(
+ 'get_groups_list',
+ status_code=status_code
+ )
+ self.try_url(
'feeds',
status_code=status_code,
kwargs={'url':'rss'})
@@ -311,12 +344,12 @@ class PageLoadTestCase(AskbotTestCase):
status_code=status_code,
template='users.html'
)
- self.try_url(
- 'widget_questions',
- status_code = status_code,
- data={'tags': 'tag-1-0'},
- template='question_widget.html',
- )
+ #self.try_url(
+ # 'widget_questions',
+ # status_code = status_code,
+ # data={'tags': 'tag-1-0'},
+ # template='question_widget.html',
+ # )
#todo: really odd naming conventions for sort methods
self.try_url(
'users',
@@ -409,10 +442,9 @@ class PageLoadTestCase(AskbotTestCase):
@skipIf('askbot.middleware.forum_mode.ForumModeMiddleware' \
not in settings.MIDDLEWARE_CLASSES,
'no ForumModeMiddleware set')
+ @with_settings({'ASKBOT_CLOSED_FORUM_MODE': True})
def test_non_user_urls_in_closed_forum_mode(self):
- askbot_settings.ASKBOT_CLOSED_FORUM_MODE = True
self.proto_test_non_user_urls(status_code=302)
- askbot_settings.ASKBOT_CLOSED_FORUM_MODE = False
#def test_non_user_urls_logged_in(self):
#user = User.objects.get(id=1)
@@ -481,11 +513,9 @@ class PageLoadTestCase(AskbotTestCase):
@skipIf('askbot.middleware.forum_mode.ForumModeMiddleware' \
not in settings.MIDDLEWARE_CLASSES,
'no ForumModeMiddleware set')
+ @with_settings({'ASKBOT_CLOSED_FORUM_MODE': True})
def test_user_urls_in_closed_forum_mode(self):
- askbot_settings.ASKBOT_CLOSED_FORUM_MODE = True
self.proto_test_user_urls(status_code=302)
- askbot_settings.ASKBOT_CLOSED_FORUM_MODE = False
-
def test_user_urls_logged_in(self):
user = models.User.objects.get(id=2) # INFO: Hardcoded ID, might fail if DB allocates IDs in some non-continuous way
@@ -516,9 +546,16 @@ class PageLoadTestCase(AskbotTestCase):
'user_profile',
kwargs={'id': asker.id, 'slug': slugify(asker.username)},
data={'sort':'inbox'},
- template='user_profile/user_inbox.html',
+ template='user_inbox/responses_and_flags.html',
)
+ @with_settings({'GROUPS_ENABLED': True})
+ def test_user_page_with_groups_enabled(self):
+ self.try_url('users', status_code=302)
+
+ @with_settings({'GROUPS_ENABLED': False})
+ def test_user_page_with_groups_disabled(self):
+ self.try_url('users', status_code=200)
class AvatarTests(AskbotTestCase):
@@ -614,16 +651,60 @@ class QuestionPageRedirectTests(AskbotTestCase):
self.assertRedirects(resp, expected_url = self.q.get_absolute_url())
class CommandViewTests(AskbotTestCase):
- def test_get_tag_wiki_text_succeeds(self):
- tag1 = self.create_tag('tag1')
+ def test_load_empty_object_description_works(self):
+ group = models.Group(name='somegroup')
+ group.save()
+
+ response = self.client.get(
+ reverse('load_object_description'),
+ data = {'object_id': group.id,'model_name': 'Group'},
+ )
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, '')
+
+ def test_load_full_object_description_works(self):
+ group = models.Group(name='somegroup')
+ user = self.create_user('someuser')
+ post_params = {'author': user, 'text':'some text'}
+ post = models.Post.objects.create_new_tag_wiki(**post_params)
+ group.description = post
+ group.save()
+
response = self.client.get(
- reverse('load_tag_wiki_text'),
- data = {'tag_id': tag1.id}
+ reverse('load_object_description'),
+ data = {'object_id': group.id,'model_name': 'Group'},
)
self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, 'some text')
+
+ def test_save_object_description_works(self):
+ group = models.Group(name='somegroup')
+ group.save()
+ admin = self.create_user('admin', status='d')
+ self.client.login(user_id=admin.id, method='force')
+ post_data = {
+ 'object_id': group.id,
+ 'model_name': 'Group',
+ 'text': 'some description'
+ }
+ self.client.post(#ajax post
+ reverse('save_object_description'),
+ data=post_data,
+ HTTP_X_REQUESTED_WITH='XMLHttpRequest'
+ )
+ group = self.reload_object(group)
+ self.assertEqual(group.description.text, 'some description')
+
+ #test edit
+ post_data['text'] = 'edited description'
+ self.client.post(#second post to edit
+ reverse('save_object_description'),
+ data=post_data,
+ HTTP_X_REQUESTED_WITH='XMLHttpRequest'
+ )
+ group = self.reload_object(group)
+ self.assertEqual(group.description.text, 'edited description')
- def test_get_tag_wiki_text_fails(self):
- tag1 = self.create_tag('tag1')
- response = self.client.get(reverse('load_tag_wiki_text'))
- self.assertEqual(response.status_code, 400)#bad request
-
+ def test_load_object_description_fails(self):
+ response = self.client.get(reverse('load_object_description'))
+ self.assertEqual(response.status_code, 404)#bad request
diff --git a/askbot/tests/permission_assertion_tests.py b/askbot/tests/permission_assertion_tests.py
index 8061bdb8..3849ce90 100644
--- a/askbot/tests/permission_assertion_tests.py
+++ b/askbot/tests/permission_assertion_tests.py
@@ -1590,7 +1590,10 @@ class ClosedForumTests(utils.AskbotTestCase):
self.test_url = self.question.get_absolute_url()
self.redirect_to = settings.LOGIN_URL
self.client = Client()
- askbot_settings.ASKBOT_CLOSED_FORUM_MODE = True
+ askbot_settings.update('ASKBOT_CLOSED_FORUM_MODE', True)
+
+ def tearDown(self):
+ askbot_settings.update('ASKBOT_CLOSED_FORUM_MODE', False)
@skipIf('askbot.middleware.forum_mode.ForumModeMiddleware' \
not in settings.MIDDLEWARE_CLASSES,
@@ -1615,6 +1618,3 @@ class ClosedForumTests(utils.AskbotTestCase):
self.client.login(username=self.other_user.username, password=self.password)
response = self.client.get(self.test_url)
self.assertEquals(response.status_code, 200)
-
- def tearDown(self):
- askbot_settings.ASKBOT_CLOSED_FORUM_MODE = False
diff --git a/askbot/tests/post_model_tests.py b/askbot/tests/post_model_tests.py
index a665e444..1a3a9c49 100644
--- a/askbot/tests/post_model_tests.py
+++ b/askbot/tests/post_model_tests.py
@@ -11,9 +11,14 @@ from django.core.cache.backends.locmem import LocMemCache
from django.core.exceptions import ValidationError
from askbot.tests.utils import AskbotTestCase
-from askbot.models import Post, PostRevision, Thread, Tag
+from askbot.models import Post
+from askbot.models import PostRevision
+from askbot.models import Thread
+from askbot.models import Tag
+from askbot.models import Group
from askbot.search.state_manager import DummySearchState
from django.utils import simplejson
+from askbot.conf import settings as askbot_settings
class PostModelTests(AskbotTestCase):
@@ -133,6 +138,36 @@ class PostModelTests(AskbotTestCase):
self.assertTrue(p._thread_cache is th)
self.assertEqual('/question/3/lala-x-lala/', p.get_absolute_url(thread=th))
+ def test_get_moderators_with_groups(self):
+ groups_enabled_backup = askbot_settings.GROUPS_ENABLED
+ askbot_settings.update('GROUPS_ENABLED', True)
+ #create group
+ group = Group(name='testers', openness=Group.OPEN)
+ group.save()
+
+ #create one admin and one moderator, and one reg user
+ mod1 = self.create_user('mod1', status='m')
+ adm1 = self.create_user('adm1', status='d')
+ reg1 = self.create_user('reg1')
+ #join them to the group
+ mod1.join_group(group)
+ adm1.join_group(group)
+ reg1.join_group(group)
+ #create one admin and one moderator, and one reg user
+ mod2 = self.create_user('mod2', status='m')
+ adm2 = self.create_user('adm2', status='d')
+ reg2 = self.create_user('reg2')
+ #make a post
+ question = self.post_question(user=reg1, group_id=group.id)
+ #run get_moderators and see that only one admin and one
+ mods = question.get_moderators()
+ self.assertEqual(
+ set([mod1, adm1]),
+ set(mods)
+ )
+ #moderator are in the set of moderators
+ askbot_settings.update('GROUPS_ENABLED', groups_enabled_backup)
+
class ThreadTagModelsTests(AskbotTestCase):
@@ -285,6 +320,7 @@ class ThreadRenderLowLevelCachingTests(AskbotTestCase):
'thread': thread,
'question': thread._question_post(),
'search_state': ss,
+ 'visitor': None
}
proper_html = get_template('widgets/question_summary.html').render(context)
self.assertEqual(test_html, proper_html)
@@ -406,7 +442,8 @@ class ThreadRenderCacheUpdateTests(AskbotTestCase):
'thread': q.thread,
'question': q,
'search_state': DummySearchState(),
- }
+ 'visitor': None
+ }
html = get_template('widgets/question_summary.html').render(context)
return html
diff --git a/askbot/tests/thread_model_tests.py b/askbot/tests/thread_model_tests.py
index 3f313ff3..baf51e8e 100644
--- a/askbot/tests/thread_model_tests.py
+++ b/askbot/tests/thread_model_tests.py
@@ -1,7 +1,9 @@
from askbot.tests.utils import AskbotTestCase
from askbot.conf import settings as askbot_settings
from askbot import models
+from askbot.models.tag import get_global_group
import django.core.mail
+from django.core.urlresolvers import reverse
class ThreadModelTestsWithGroupsEnabled(AskbotTestCase):
@@ -19,9 +21,7 @@ class ThreadModelTestsWithGroupsEnabled(AskbotTestCase):
'm_and_c': 'i'
}
)
- self.group = models.Tag.group_tags.get_or_create(
- group_name = 'jockeys', user = self.admin
- )
+ self.group = models.Group.objects.get_or_create(name='jockeys')
self.admin.edit_group_membership(
group = self.group,
user = self.admin,
@@ -59,3 +59,71 @@ class ThreadModelTestsWithGroupsEnabled(AskbotTestCase):
self.assertEqual(len(django.core.mail.outbox), 1)
user = self.reload_object(self.user)
self.assertEqual(user.new_response_count, 1)
+
+ def test_answer_to_private_question_is_not_globally_visible(self):
+ question = self.post_question(user=self.admin, is_private=True)
+ answer = self.post_answer(question=question, user=self.admin, is_private=False)
+ global_group = get_global_group()
+ self.assertEqual(
+ global_group in set(answer.groups.all()),
+ False
+ )
+
+ def test_answer_to_group_question_is_not_globally_visible(self):
+ #ask into group where user is not a member
+ question = self.post_question(user=self.user, group_id=self.group.id)
+ #answer posted by a group member
+ answer = self.post_answer(question=question, user=self.admin, is_private=False)
+ global_group = get_global_group()
+ self.assertEqual(
+ global_group in set(answer.groups.all()),
+ False
+ )
+
+
+ def test_restrictive_response_publishing(self):
+ #restrictive model should work even with groups
+ #in common between the asker and the answerer
+ common_group = models.Group(
+ name='common',
+ openness=models.Group.OPEN
+ )
+ common_group.save()
+ self.admin.join_group(common_group)
+ self.user.join_group(common_group)
+
+ self.group.moderate_answers_to_enquirers = True
+ self.group.save()
+ question = self.post_question(user=self.user, group_id=self.group.id)
+ answer = self.post_answer(question=question, user=self.admin)
+
+ #answer and the user don't have groups in common
+ answer_groups = set(answer.groups.all())
+ user_groups = set(self.user.get_groups())
+ self.assertEqual(len(answer_groups & user_groups), 0)
+
+ #publish the answer
+ self.client.login(user_id=self.admin.id, method='force')
+ self.client.post(
+ reverse('publish_answer'),
+ data={'answer_id': answer.id},
+ HTTP_X_REQUESTED_WITH='XMLHttpRequest'
+ )
+ #todo: test redirect
+
+ answer = self.reload_object(answer)
+ answer_groups = set(answer.groups.all())
+ self.assertEqual(len(answer_groups & user_groups), 1)
+
+
+
+ def test_permissive_response_publishing(self):
+ self.group.moderate_answers_to_enquirers = False
+ self.group.save()
+ question = self.post_question(user=self.user, group_id=self.group.id)
+ answer = self.post_answer(question=question, user=self.admin)
+
+ #answer and user have one group in common
+ answer_groups = set(answer.groups.all())
+ user_groups = set(self.user.get_groups())
+ self.assertEqual(len(answer_groups & user_groups), 1)
diff --git a/askbot/tests/user_model_tests.py b/askbot/tests/user_model_tests.py
index 7a8fd9f2..df4974dd 100644
--- a/askbot/tests/user_model_tests.py
+++ b/askbot/tests/user_model_tests.py
@@ -9,12 +9,8 @@ class UserModelTests(AskbotTestCase):
def test_new_user_has_personal_group(self):
user = User.objects.create_user('somebody', 'somebody@example.com')
group_name = format_personal_group_name(user)
- group = models.Tag.objects.filter(name=group_name)
+ group = models.Group.objects.filter(name=group_name)
self.assertEqual(group.count(), 1)
-
- group_profile = models.GroupProfile.objects.filter(group_tag=group)
- self.assertEqual(group_profile.count(), 1)
-
memberships = models.GroupMembership.objects.filter(
group=group, user=user
)
diff --git a/askbot/tests/utils.py b/askbot/tests/utils.py
index dd46e31d..1cd174c1 100644
--- a/askbot/tests/utils.py
+++ b/askbot/tests/utils.py
@@ -4,9 +4,41 @@ from django.test import TestCase
from functools import wraps
from askbot import models
+def with_settings(settings_dict):
+ """a decorator that will run function with settings
+ then apply previous settings and return the result
+ of the function.
+ If the function raises an exception - decorator
+ still restores the previous settings
+ """
+
+ def decorator(func):
+
+ @wraps(func)
+ def wrapped(*args, **kwargs):
+ from askbot.conf import settings as askbot_settings
+ backup_settings_dict = dict()
+ for key, value in settings_dict.items():
+ backup_settings_dict[key] = getattr(askbot_settings, key)
+ askbot_settings.update(key, value)
+
+ try:
+ return func(*args, **kwargs)
+ except:
+ raise
+ finally:
+ for key, value in backup_settings_dict.items():
+ askbot_settings.update(key, value)
+
+ return wrapped
+
+ return decorator
+
+
+
def create_user(
- username = None,
- email = None,
+ username = None,
+ email = None,
notification_schedule = None,
date_joined = None,
status = 'a',
@@ -25,13 +57,13 @@ def create_user(
* 'q_sel' - questions that user decides to follow
* 'm_and_c' - comments and mentions of user anywhere
- and values as keys in
+ and values as keys in
:attr:`~askbot.models.EmailFeedSetting.FEED_TYPES`:
* 'i' - instantly
* 'd' - daily
* 'w' - weekly
- * 'n' - never
+ * 'n' - never
"""
user = models.User.objects.create_user(username, email)
@@ -42,7 +74,7 @@ def create_user(
user.set_status(status)
if notification_schedule == None:
notification_schedule = models.EmailFeedSetting.NO_EMAIL_SCHEDULE
-
+
#a hack, we need to delete these, that will be created automatically
#because just below we will be replacing them with the new values
user.notification_subscriptions.all().delete()
@@ -107,9 +139,16 @@ class AskbotTestCase(TestCase):
args_list.pop(1)#so we can remove an item
self.assertRaises(*args_list, **kwargs)
+ def assertQuerysetEqual(self, qs1, qs2, transform=repr, ordered=True):
+ '''borrowed from django1.4 and modified a bit'''
+ items = map(transform, qs1)
+ values = map(transform, qs2)
+ if not ordered:
+ return self.assertEqual(set(items), set(values))
+ return self.assertEqual(list(items), list(values))
def post_question(
- self,
+ self,
user = None,
title = 'test question title',
body_text = 'test question body text',
@@ -207,7 +246,7 @@ class AskbotTestCase(TestCase):
"""reloads model object from the database
"""
return obj.__class__.objects.get(id = obj.id)
-
+
def post_answer(
self,
user = None,
@@ -244,11 +283,10 @@ class AskbotTestCase(TestCase):
tag.save()
return tag
- def create_group(self, group_name=None, user=None):
- return models.Tag.group_tags.get_or_create(
- group_name='private',
- user=self.u1
- )
+ def create_group(self, group_name=None, openness=models.Group.OPEN):
+ return models.Group.objects.get_or_create(
+ name='private', openness=openness
+ )
def post_comment(
self,
@@ -258,8 +296,8 @@ class AskbotTestCase(TestCase):
by_email = False,
timestamp = None
):
- """posts and returns a comment to parent post, uses
- now timestamp if not given, dummy body_text
+ """posts and returns a comment to parent post, uses
+ now timestamp if not given, dummy body_text
author is required
"""
if user is None:
diff --git a/askbot/tests/utils_tests.py b/askbot/tests/utils_tests.py
new file mode 100644
index 00000000..7f252b69
--- /dev/null
+++ b/askbot/tests/utils_tests.py
@@ -0,0 +1,17 @@
+from django.test import TestCase
+from askbot.utils.url_utils import urls_equal
+
+class UrlUtilsTests(TestCase):
+
+ def tests_urls_equal(self):
+ e = urls_equal
+ self.assertTrue(e('', ''))
+ self.assertTrue(e('', '/', True))
+ self.assertTrue(e('http://cnn.com', 'http://cnn.com/', True))
+
+ self.assertFalse(e('https://cnn.com', 'http://cnn.com'))
+ self.assertFalse(e('http://cnn.com:80', 'http://cnn.com:8000'))
+
+ self.assertTrue(e('http://cnn.com/path', 'http://cnn.com/path/', True))
+ self.assertFalse(e('http://cnn.com/path', 'http://cnn.com/path/'))
+
diff --git a/askbot/tests/view_context_tests.py b/askbot/tests/view_context_tests.py
new file mode 100644
index 00000000..4c3713d0
--- /dev/null
+++ b/askbot/tests/view_context_tests.py
@@ -0,0 +1,30 @@
+from django.contrib.auth.models import AnonymousUser
+from askbot.tests.utils import AskbotTestCase
+from askbot.models import Group
+from askbot.views import context
+
+class ViewContextTests(AskbotTestCase):
+ def test_get_for_inbox_anonymous(self):
+ anon = AnonymousUser()
+ inbox_context = context.get_for_inbox(anon)
+ self.assertEqual(inbox_context, None)
+
+ def test_get_for_inbox_group_join(self):
+ mod = self.create_user('mod', status='d')
+ group = Group(name='grp', openness=Group.MODERATED)
+ group.save()
+ mod.join_group(group)
+
+ simple = self.create_user('simple')
+ simple.join_group(group)
+
+ inbox_context = context.get_for_inbox(mod)
+
+ self.assertEqual(inbox_context['re_count'], 0)
+ self.assertEqual(inbox_context['flags_count'], 0)
+ self.assertEqual(inbox_context['group_join_requests_count'], 1)
+
+ inbox_context = context.get_for_inbox(simple)
+ values = set(inbox_context.values())
+ self.assertEqual(values, set([0, 0, 0]))
+
diff --git a/askbot/tests/widget_tests.py b/askbot/tests/widget_tests.py
index 40c63e0e..98c5a8aa 100644
--- a/askbot/tests/widget_tests.py
+++ b/askbot/tests/widget_tests.py
@@ -11,6 +11,7 @@ class WidgetViewsTests(AskbotTestCase):
def setUp(self):
self.client = Client()
+ self.widget = models.AskWidget.objects.create(title='foo widget')
self.user = self.create_user('user1')
self.user.set_password('sample')
self.user.save()
@@ -19,12 +20,13 @@ class WidgetViewsTests(AskbotTestCase):
def test_post_with_auth(self):
self.client.login(username='user1', password='sample')
- response = self.client.post(reverse('ask_by_widget'), self.good_data)
+ response = self.client.post(reverse('ask_by_widget', args=(self.widget.id, )), self.good_data)
self.assertEquals(response.status_code, 302)
self.client.logout()
def test_post_without_auth(self):
- response = self.client.post(reverse('ask_by_widget'), self.good_data)
+ #weird issue
+ response = self.client.post(reverse('ask_by_widget', args=(self.widget.id, )), self.good_data)
self.assertEquals(response.status_code, 302)
self.assertTrue('widget_question' in self.client.session)
self.assertEquals(self.client.session['widget_question']['title'],
@@ -45,12 +47,21 @@ class WidgetViewsTests(AskbotTestCase):
session = self.client.session
session['widget_question'] = widget_question_data
session.save()
- response = self.client.get(reverse('ask_by_widget'),
- {'action': 'post-after-login'})
+ response = self.client.get(
+ reverse('ask_by_widget', args=(self.widget.id, )),
+ {'action': 'post-after-login'}
+ )
self.assertFalse('widget_question' in self.client.session)
self.assertEquals(response.status_code, 302)
#verify posting question
+ def test_render_widget_view(self):
+ response = self.client.get(reverse('render_ask_widget', args=(self.widget.id, )))
+ self.assertEquals(200, response.status_code)
+ mimetype = 'text/javascript'
+ self.assertTrue(mimetype in response['Content-Type'])
+
+
class WidgetLoginViewTest(AskbotTestCase):
def test_correct_template_loading(self):
@@ -59,3 +70,100 @@ class WidgetLoginViewTest(AskbotTestCase):
template_name = 'authopenid/widget_signin.html'
templates = [template.name for template in response.templates]
self.assertTrue(template_name in templates)
+
+class WidgetCreatorViewsTests(AskbotTestCase):
+
+ def setUp(self):
+ self.client = Client()
+ self.user = self.create_user('user1')
+ self.user.set_password('testpass')
+ self.user.set_admin_status()
+ self.user.save()
+ self.widget = models.AskWidget.objects.create(title='foo widget')
+
+ def test_list_ask_widget_view(self):
+ self.client.login(username='user1', password='testpass')
+ response = self.client.get(reverse('list_widgets', args=('ask',)))
+ self.assertEquals(response.status_code, 200)
+ self.assertTrue('widgets' in response.context)
+
+ def test_create_ask_widget_get(self):
+ self.client.login(username='user1', password='testpass')
+ response = self.client.get(reverse('create_widget', args=('ask',)))
+ self.assertEquals(response.status_code, 200)
+ self.assertTrue('form' in response.context)
+
+ def test_create_ask_widget_post(self):
+ self.client.login(username='user1', password='testpass')
+ post_data = {'title': 'Test widget'}
+ response = self.client.post(reverse('create_widget', args=('ask',)), post_data)
+ self.assertEquals(response.status_code, 302)
+
+ def test_edit_ask_widget_get(self):
+ self.client.login(username='user1', password='testpass')
+ response = self.client.get(reverse('edit_widget',
+ args=('ask', self.widget.id, )))
+ self.assertEquals(response.status_code, 200)
+ self.assertTrue('form' in response.context)
+
+ def test_edit_ask_widget_post(self):
+ self.client.login(username='user1', password='testpass')
+ post_data = {'title': 'Test lalalla'}
+ response = self.client.post(reverse('edit_widget',
+ args=('ask', self.widget.id, )), post_data)
+ self.assertEquals(response.status_code, 302)
+
+ def test_delete_ask_widget_get(self):
+ self.client.login(username='user1', password='testpass')
+ response = self.client.get(reverse('delete_widget',
+ args=('ask', self.widget.id, )))
+ self.assertEquals(response.status_code, 200)
+ self.assertTrue('widget' in response.context)
+
+ def test_delete_ask_widget_post(self):
+ self.client.login(username='user1', password='testpass')
+ response = self.client.post(reverse('delete_widget',
+ args=('ask', self.widget.id, )))
+ self.assertEquals(response.status_code, 302)
+
+ #this test complains about 404.html template but it's correct
+ #def test_bad_url(self):
+ # self.client.login(username='user1', password='testpass')
+ # response = self.client.get('/widgets/foo/create/')
+ # self.assertEquals(404, response.status_code)
+
+
+class QuestionWidgetViewsTests(AskbotTestCase):
+
+ def setUp(self):
+ self.user = self.create_user('testuser')
+ self.client = Client()
+ self.widget = models.QuestionWidget.objects.create(title="foo",
+ question_number=5, search_query='test',
+ tagnames='test')
+
+ #we post 6 questions!
+ titles = (
+ 'test question 1', 'this is a test',
+ 'without the magic word', 'test test test',
+ 'test just another test', 'no magic word',
+ 'test another', 'I can no believe is a test'
+ )
+
+ tagnames = 'test foo bar'
+ for title in titles:
+ self.post_question(title=title, tags=tagnames)
+
+ def test_valid_response(self):
+ filter_params = {
+ 'title__icontains': self.widget.search_query,
+ 'tags__name__in': self.widget.tagnames.split(' ')
+ }
+
+ threads = models.Thread.objects.filter(**filter_params)[:5]
+
+ response = self.client.get(reverse('question_widget', args=(self.widget.id, )))
+ self.assertEquals(200, response.status_code)
+
+ self.assertQuerysetEqual(threads, response.context['threads'])
+ self.assertEquals(self.widget, response.context['widget'])
diff --git a/askbot/urls.py b/askbot/urls.py
index cee5752a..39242a1b 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -70,13 +70,27 @@ urlpatterns = patterns('',
views.readers.questions,
name='questions'
),
-
# END main page urls
url(
r'^api/get_questions/',
views.commands.api_get_questions,
- name = 'api_get_questions'
+ name='api_get_questions'
+ ),
+ url(
+ r'^get-thread-shared-users/',
+ views.commands.get_thread_shared_users,
+ name='get_thread_shared_users'
+ ),
+ url(
+ r'^get-thread-shared-groups/',
+ views.commands.get_thread_shared_groups,
+ name='get_thread_shared_groups'
+ ),
+ url(
+ r'^moderate-group-join-request/',
+ views.commands.moderate_group_join_request,
+ name='moderate_group_join_request'
),
url(
r'^save-draft-question/',
@@ -104,6 +118,11 @@ urlpatterns = patterns('',
name='get_users_info'
),
url(
+ r'^get-editor/',
+ views.commands.get_editor,
+ name='get_editor'
+ ),
+ url(
r'^%s%s$' % (_('questions/'), _('ask/')),
views.writers.ask,
name='ask'
@@ -144,11 +163,6 @@ urlpatterns = patterns('',
kwargs = {'post_type': 'question'},
name='question_revisions'
),
- url(
- r'^%s%s$' % (_('widgets/'), _('questions/')),
- views.readers.widget_questions,
- name='widget_questions'
- ),
url(#ajax only
r'^comment/upvote/$',
views.commands.upvote_comment,
@@ -179,6 +193,21 @@ urlpatterns = patterns('',
views.readers.get_comment,
name='get_comment'
),
+ url(#post only
+ r'^comment/convert/$',
+ views.writers.comment_to_answer,
+ name='comment_to_answer'
+ ),
+ url(#post only
+ r'^answer/convert/$',
+ views.writers.answer_to_comment,
+ name='answer_to_comment'
+ ),
+ url(#post only
+ r'^answer/publish/$',
+ views.commands.publish_answer,
+ name='publish_answer'
+ ),
url(
r'^%s$' % _('tags/'),
views.readers.tags,
@@ -235,14 +264,14 @@ urlpatterns = patterns('',
name = 'get_tag_list'
),
url(
- r'^load-tag-wiki-text/',
- views.commands.load_tag_wiki_text,
- name = 'load_tag_wiki_text'
+ r'^load-object-description/',
+ views.commands.load_object_description,
+ name = 'load_object_description'
),
url(#ajax only
- r'^save-tag-wiki-text/',
- views.commands.save_tag_wiki_text,
- name = 'save_tag_wiki_text'
+ r'^save-object-description/',
+ views.commands.save_object_description,
+ name = 'save_object_description'
),
url(#ajax only
r'^add-tag-category/',
@@ -270,9 +299,19 @@ urlpatterns = patterns('',
name = 'delete_group_logo'
),
url(#ajax only
+ r'^add-group/',
+ views.commands.add_group,
+ name = 'add_group'
+ ),
+ url(#ajax only
r'^toggle-group-profile-property/',
views.commands.toggle_group_profile_property,
- name = 'toggle_group_profile_property'
+ name='toggle_group_profile_property'
+ ),
+ url(#ajax only
+ r'^set-group-openness/',
+ views.commands.set_group_openness,
+ name='set_group_openness'
),
url(#ajax only
r'^edit-object-property-text/',
@@ -382,16 +421,59 @@ urlpatterns = patterns('',
),
#widgets url!
url(
- r'^widgets/ask/$',
+ r'^%s$' % (_('widgets/')),
+ views.widgets.widgets,
+ name = 'widgets'
+ ),
+
+ url(
+ r'^%s%s(?P<widget_id>\d+)/$' % (_('widgets/'), _('ask/')),
views.widgets.ask_widget,
name = 'ask_by_widget'
),
url(
- r'^widgets/ask/complete/$',
+ r'^%s%s(?P<widget_id>\d+).js$' % (_('widgets/'), _('ask/')),
+ views.widgets.render_ask_widget_js,
+ name = 'render_ask_widget'
+ ),
+ url(
+ r'^%s%s(?P<widget_id>\d+).css$' % (_('widgets/'), _('ask/')),
+ views.widgets.render_ask_widget_css,
+ name = 'render_ask_widget_css'
+ ),
+
+ url(
+ r'^%s%s%s$' % (_('widgets/'), _('ask/'), _('complete/')),
views.widgets.ask_widget_complete,
name = 'ask_by_widget_complete'
),
url(
+ r'^%s(?P<model>\w+)/%s$' % (_('widgets/'), _('create/')),
+ views.widgets.create_widget,
+ name = 'create_widget'
+ ),
+ url(
+ r'^%s(?P<model>\w+)/%s(?P<widget_id>\d+)/$' % (_('widgets/'), _('edit/')),
+ views.widgets.edit_widget,
+ name = 'edit_widget'
+ ),
+ url(
+ r'^%s(?P<model>\w+)/%s(?P<widget_id>\d+)/$' % (_('widgets/'), _('delete/')),
+ views.widgets.delete_widget,
+ name = 'delete_widget'
+ ),
+
+ url(
+ r'^%s(?P<model>\w+)/$' % (_('widgets/')),
+ views.widgets.list_widgets,
+ name = 'list_widgets'
+ ),
+ url(
+ r'^widgets/questions/(?P<widget_id>\d+)/$',
+ views.widgets.question_widget,
+ name = 'question_widget'
+ ),
+ url(
r'^feeds/(?P<url>.*)/$',
'django.contrib.syndication.views.feed',
{'feed_dict': feeds},
diff --git a/askbot/utils/console.py b/askbot/utils/console.py
index a7787325..a691d961 100644
--- a/askbot/utils/console.py
+++ b/askbot/utils/console.py
@@ -2,8 +2,16 @@
"""
import sys
import time
+import logging
from askbot.utils import path
+def start_printing_db_queries():
+ """starts logging database queries into console,
+ should be used for debugging only"""
+ logger = logging.getLogger('django.db.backends')
+ logger.setLevel(logging.DEBUG)
+ logger.addHandler(logging.StreamHandler())
+
def choice_dialog(prompt_phrase, choices = None, invalid_phrase = None):
"""prints a prompt, accepts keyboard input
and makes sure that user response is one of given
diff --git a/askbot/utils/forms.py b/askbot/utils/forms.py
index 81c10d71..6c3af066 100644
--- a/askbot/utils/forms.py
+++ b/askbot/utils/forms.py
@@ -3,10 +3,13 @@ from django import forms
from django.http import str_to_unicode
from django.contrib.auth.models import User
from django.conf import settings
+from django.http import Http404
+from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
from askbot.conf import settings as askbot_settings
from askbot.utils.slug import slugify
+from askbot.utils.functions import split_list
from askbot import const
from longerusername import MAX_USERNAME_LENGTH
import logging
@@ -27,6 +30,23 @@ def clean_next(next, default = None):
def get_next_url(request, default = None):
return clean_next(request.REQUEST.get('next'), default)
+def get_db_object_or_404(params):
+ """a utility function that returns an object
+ in return to the model_name and object_id
+
+ only specific models are accessible
+ """
+ from askbot import models
+ try:
+ model_name = params['model_name']
+ assert(model_name=='Group')
+ model = models.get_model(model_name)
+ obj_id = forms.IntegerField().clean(params['object_id'])
+ return get_object_or_404(model, id=obj_id)
+ except Exception:
+ #need catch-all b/c of the nature of the function
+ raise Http404
+
class StrippedNonEmptyCharField(forms.CharField):
def clean(self, value):
value = value.strip()
@@ -59,6 +79,7 @@ class UserNameField(StrippedNonEmptyCharField):
must_exist=False,
skip_clean=False,
label=_('Choose a screen name'),
+ widget_attrs=None,
**kw
):
self.must_exist = must_exist
@@ -80,13 +101,19 @@ class UserNameField(StrippedNonEmptyCharField):
error_messages.update(kw['error_messages'])
del kw['error_messages']
+ if widget_attrs:
+ widget_attrs.update(login_form_widget_attrs)
+ else:
+ widget_attrs = login_form_widget_attrs
+
max_length = MAX_USERNAME_LENGTH()
- super(UserNameField,self).__init__(max_length=max_length,
+ super(UserNameField,self).__init__(
+ max_length=max_length,
widget=forms.TextInput(attrs=login_form_widget_attrs),
label=label,
error_messages=error_messages,
**kw
- )
+ )
def clean(self,username):
""" validate username """
@@ -144,25 +171,63 @@ class UserNameField(StrippedNonEmptyCharField):
logging.debug('error - user with this name already exists')
raise forms.ValidationError(self.error_messages['multiple-taken'])
+
+def email_is_allowed(
+ email, allowed_emails='', allowed_email_domains=''
+):
+ """True, if email address is pre-approved or matches a allowed
+ domain"""
+ if allowed_emails:
+ email_list = split_list(allowed_emails)
+ allowed_emails = ' ' + ' '.join(email_list) + ' '
+ email_match_re = re.compile(r'\s%s\s' % email)
+ if email_match_re.search(allowed_emails):
+ return True
+
+ if allowed_email_domains:
+ email_domain = email.split('@')[1]
+ domain_list = split_list(allowed_email_domains)
+ domain_match_re = re.compile(r'\s%s\s' % email_domain)
+ allowed_email_domains = ' ' + ' '.join(domain_list) + ' '
+ return domain_match_re.search(allowed_email_domains)
+
+ return False
+
class UserEmailField(forms.EmailField):
def __init__(self,skip_clean=False,**kw):
self.skip_clean = skip_clean
- super(UserEmailField,self).__init__(widget=forms.TextInput(attrs=dict(login_form_widget_attrs,
- maxlength=200)), label=mark_safe(_('Your email <i>(never shared)</i>')),
- error_messages={'required':_('email address is required'),
- 'invalid':_('please enter a valid email address'),
- 'taken':_('this email is already used by someone else, please choose another'),
- },
+ super(UserEmailField,self).__init__(
+ widget=forms.TextInput(
+ attrs=dict(login_form_widget_attrs, maxlength=200)
+ ),
+ label=mark_safe(_('Your email <i>(never shared)</i>')),
+ error_messages={
+ 'required':_('email address is required'),
+ 'invalid':_('please enter a valid email address'),
+ 'taken':_('this email is already used by someone else, please choose another'),
+ 'unauthorized':_('this email address is not authorized')
+ },
**kw
- )
+ )
- def clean(self,email):
+ def clean(self, email):
""" validate if email exist in database
from legacy register
return: raise error if it exist """
email = super(UserEmailField,self).clean(email.strip())
if self.skip_clean:
return email
+
+ allowed_domains = askbot_settings.ALLOWED_EMAIL_DOMAINS.strip()
+ allowed_emails = askbot_settings.ALLOWED_EMAILS.strip()
+
+ if allowed_emails or allowed_domains:
+ if not email_is_allowed(
+ email,
+ allowed_emails=allowed_emails,
+ allowed_email_domains=allowed_domains
+ ):
+ raise forms.ValidationError(self.error_messages['unauthorized'])
if askbot_settings.EMAIL_UNIQUE == True:
try:
user = User.objects.get(email = email)
diff --git a/askbot/utils/url_utils.py b/askbot/utils/url_utils.py
index 6027d096..c58239c5 100644
--- a/askbot/utils/url_utils.py
+++ b/askbot/utils/url_utils.py
@@ -1,3 +1,4 @@
+import os
import urlparse
from django.core.urlresolvers import reverse
from django.conf import settings
@@ -13,6 +14,38 @@ def strip_path(url):
)
)
+def append_trailing_slash(urlpath):
+ """if path is empty - returns slash
+ if not and path does not end with the slash
+ appends it
+ """
+ if urlpath == '':
+ return '/'
+ elif not urlpath.endswith('/'):
+ return urlpath + '/'
+ return urlpath
+
+def urls_equal(url1, url2, ignore_trailing_slash=False):
+ """True, if urls are equal"""
+ purl1 = urlparse.urlparse(url1)
+ purl2 = urlparse.urlparse(url2)
+ if purl1.scheme != purl2.scheme:
+ return False
+
+ if purl1.netloc != purl2.netloc:
+ return False
+
+ if ignore_trailing_slash is True:
+ normfunc = append_trailing_slash
+ else:
+ normfunc = lambda v: v
+
+ if normfunc(purl1.path) != normfunc(purl2.path):
+ return False
+
+ #test remaining items in the parsed url
+ return purl1[3:] == purl2[3:]
+
def get_login_url():
"""returns internal login url if
django_authopenid is used, or
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index 190dcf28..bd04dd99 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -7,6 +7,7 @@ is not always very clean.
"""
import datetime
import logging
+from bs4 import BeautifulSoup
from django.conf import settings as django_settings
from django.core import exceptions
#from django.core.management import call_command
@@ -20,6 +21,7 @@ from django.utils import simplejson
from django.utils.html import escape
from django.utils.translation import ugettext as _
from django.utils.translation import string_concat
+from askbot.utils.slug import slugify
from askbot import models
from askbot import forms
from askbot.conf import should_show_sort_by_relevance
@@ -28,8 +30,11 @@ from askbot.models.tag import get_global_group
from askbot.utils import category_tree
from askbot.utils import decorators
from askbot.utils import url_utils
+from askbot.utils.forms import get_db_object_or_404
from askbot import mail
from askbot.skins.loaders import render_into_skin, get_template
+from askbot.skins.loaders import render_into_skin_as_string
+from askbot.skins.loaders import render_text_into_skin
from askbot import const
@@ -330,7 +335,7 @@ def vote(request, id):
response_data['count'] = post.offensive_flag_count
response_data['success'] = 1
-
+
elif vote_type in ['7.6', '8.6']:
#flag question or answer
if vote_type == '7.6':
@@ -468,13 +473,47 @@ def get_tags_by_wildcard(request):
wildcard = request.GET.get('wildcard', None)
if wildcard is None:
raise Http404
-
+
matching_tags = models.Tag.objects.get_by_wildcards( [wildcard,] )
count = matching_tags.count()
names = matching_tags.values_list('name', flat = True)[:20]
re_data = simplejson.dumps({'tag_count': count, 'tag_names': list(names)})
return HttpResponse(re_data, mimetype = 'application/json')
+@decorators.get_only
+def get_thread_shared_users(request):
+ """returns snippet of html with users"""
+ thread_id = request.GET['thread_id']
+ thread_id = IntegerField().clean(thread_id)
+ thread = models.Thread.objects.get(id=thread_id)
+ users = thread.get_users_shared_with()
+ data = {
+ 'users': users,
+ }
+ html = render_into_skin_as_string('widgets/user_list.html', data, request)
+ re_data = simplejson.dumps({
+ 'html': html,
+ 'users_count': users.count(),
+ 'success': True
+ })
+ return HttpResponse(re_data, mimetype='application/json')
+
+@decorators.get_only
+def get_thread_shared_groups(request):
+ """returns snippet of html with groups"""
+ thread_id = request.GET['thread_id']
+ thread_id = IntegerField().clean(thread_id)
+ thread = models.Thread.objects.get(id=thread_id)
+ groups = thread.get_groups_shared_with()
+ data = {'groups': groups}
+ html = render_into_skin_as_string('widgets/groups_list.html', data, request)
+ re_data = simplejson.dumps({
+ 'html': html,
+ 'groups_count': groups.count(),
+ 'success': True
+ })
+ return HttpResponse(re_data, mimetype='application/json')
+
@decorators.ajax_only
def get_html_template(request):
"""returns rendered template"""
@@ -494,47 +533,42 @@ def get_tag_list(request):
"""returns tags to use in the autocomplete
function
"""
- tag_names = models.Tag.objects.filter(
+ tags = models.Tag.objects.filter(
deleted = False,
status = models.Tag.STATUS_ACCEPTED
).exclude(
name__startswith='_internal_'
- ).values_list(
+ )
+
+ tag_names = tags.values_list(
'name', flat = True
)
+
output = '\n'.join(map(escape, tag_names))
return HttpResponse(output, mimetype = 'text/plain')
@decorators.get_only
-def load_tag_wiki_text(request):
- """returns text of the tag wiki in markdown format"""
- if 'tag_id' not in request.GET:
- return HttpResponse('', status = 400)#bad request
-
- 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')
+def load_object_description(request):
+ """returns text of the object description in text"""
+ obj = get_db_object_or_404(request.GET)#askbot forms utility
+ text = getattr(obj.description, 'text', '').strip()
+ return HttpResponse(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,
+@decorators.admins_only
+def save_object_description(request):
+ """if object description 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}
+ one"""
+ obj = get_db_object_or_404(request.POST)
+ text = request.POST['text']
+ if obj.description:
+ request.user.edit_post(obj.description, body_text=text)
else:
- raise ValueError('invalid post data')
+ request.user.post_object_description(obj, body_text=text)
+ return {'html': obj.description.html}
@csrf.csrf_exempt
@decorators.ajax_only
@@ -618,16 +652,13 @@ def get_groups_list(request):
"""returns names of group tags
for the autocomplete function"""
global_group = get_global_group()
- group_names = models.Tag.group_tags.get_all().filter(
- deleted = False
+ group_names = models.Group.objects.all().exclude(
+ name__startswith='_internal_'
).exclude(
name=global_group.name
- ).exclude(
- name__startswith='_internal_'
).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')
@@ -673,13 +704,24 @@ def api_get_questions(request):
query = request.GET.get('query', '').strip()
if not query:
return HttpResponseBadRequest('Invalid query')
- threads = models.Thread.objects.get_for_query(query)
+
+ if askbot_settings.GROUPS_ENABLED:
+ threads = models.Thread.objects.get_visible(user=request.user)
+ else:
+ threads = models.Thread.objects.all()
+
+ threads = models.Thread.objects.get_for_query(
+ search_query=query,
+ qs=threads
+ )
+
if should_show_sort_by_relevance():
threads = threads.extra(order_by = ['-relevance'])
#todo: filter out deleted threads, for now there is no way
threads = threads.distinct()[:30]
thread_list = [{
'title': escape(thread.title),
+ 'url': thread.get_absolute_url(),
'answer_count': thread.get_answer_count(request.user)
} for thread in threads]
json_data = simplejson.dumps(thread_list)
@@ -843,12 +885,16 @@ def read_message(request):#marks message a read
@decorators.post_only
@decorators.admins_only
def edit_group_membership(request):
+ #todo: this call may need to go.
+ #it used to be the one creating groups
+ #from the user profile page
+ #we have a separate method
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)
+ user = models.User.objects.get(id=user_id)
except models.User.DoesNotExist:
raise exceptions.PermissionDenied(
'user with id %d not found' % user_id
@@ -857,8 +903,8 @@ def edit_group_membership(request):
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)
+ group_params = {'name': group_name, 'user': user}
+ group = models.Group.objects.get_or_create(**group_params)
request.user.edit_group_membership(user, group, 'add')
template = get_template('widgets/group_snippet.html')
return {
@@ -868,9 +914,9 @@ def edit_group_membership(request):
}
elif action == 'remove':
try:
- group = models.Tag.group_tags.get_by_name(group_name = group_name)
+ group = models.Group.objects.get(group_name = group_name)
request.user.edit_group_membership(user, group, 'remove')
- except models.Tag.DoesNotExist:
+ except models.Group.DoesNotExist:
raise exceptions.PermissionDenied()
else:
raise exceptions.PermissionDenied()
@@ -884,16 +930,34 @@ def edit_group_membership(request):
@decorators.admins_only
def save_group_logo_url(request):
"""saves urls for the group logo"""
- form = forms.GroupLogoURLForm(request.POST)
+ 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()
+ group = models.Group.objects.get(id = group_id)
+ group.logo_url = image_url
+ group.save()
else:
raise ValueError('invalid data found when saving group logo')
+@csrf.csrf_exempt
+@decorators.ajax_only
+@decorators.post_only
+@decorators.admins_only
+def add_group(request):
+ group_name = request.POST.get('group')
+ if group_name:
+ group = models.Group.objects.get_or_create(
+ name=group_name,
+ openness=models.Group.OPEN,
+ user=request.user,
+ )
+
+ url = reverse('users_by_group', kwargs={'group_id': group.id,
+ 'group_slug': slugify(group_name)})
+ response_dict = dict(group_name = group_name,
+ url = url )
+ return response_dict
@csrf.csrf_exempt
@decorators.ajax_only
@@ -901,9 +965,9 @@ def save_group_logo_url(request):
@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()
+ group = models.Group.objects.get(id = group_id)
+ group.logo_url = None
+ group.save()
@csrf.csrf_exempt
@@ -924,17 +988,28 @@ 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()
+ assert property_name in ('moderate_email', 'moderate_answers_to_enquirers')
+ group = models.Group.objects.get(id = group_id)
+ new_value = not getattr(group, property_name)
+ setattr(group, property_name, new_value)
+ group.save()
return {'is_enabled': new_value}
@csrf.csrf_exempt
@decorators.ajax_only
+@decorators.post_only
+@decorators.admins_only
+def set_group_openness(request):
+ group_id = IntegerField().clean(int(request.POST['group_id']))
+ value = IntegerField().clean(int(request.POST['value']))
+ group = models.Group.objects.get(id=group_id)
+ group.openness = value
+ group.save()
+
+
+@csrf.csrf_exempt
+@decorators.ajax_only
@decorators.admins_only
def edit_object_property_text(request):
model_name = CharField().clean(request.REQUEST['model_name'])
@@ -942,8 +1017,8 @@ def edit_object_property_text(request):
property_name = CharField().clean(request.REQUEST['property_name'])
accessible_fields = (
- ('GroupProfile', 'preapproved_emails'),
- ('GroupProfile', 'preapproved_email_domains')
+ ('Group', 'preapproved_emails'),
+ ('Group', 'preapproved_email_domains')
)
if (model_name, property_name) not in accessible_fields:
@@ -964,25 +1039,30 @@ def edit_object_property_text(request):
@decorators.ajax_only
@decorators.post_only
def join_or_leave_group(request):
- """only current user can join/leave group"""
+ """called when user wants to join/leave
+ ask to join/cancel join request, depending
+ on the groups acceptance level for the given user
+
+ returns resulting "membership_level"
+ """
if request.user.is_anonymous():
raise exceptions.PermissionDenied()
+ Group = models.Group
+ Membership = models.GroupMembership
+
group_id = IntegerField().clean(request.POST['group_id'])
- group = models.Tag.objects.get(id = group_id)
+ group = Group.objects.get(id=group_id)
- if request.user.is_group_member(group):
- action = 'remove'
- is_member = False
+ membership = request.user.get_group_membership(group)
+ if membership is None:
+ membership = request.user.join_group(group)
+ new_level = membership.get_level_display()
else:
- action = 'add'
- is_member = True
- request.user.edit_group_membership(
- user = request.user,
- group = group,
- action = action
- )
- return {'is_member': is_member}
+ membership.delete()
+ new_level = Membership.get_level_value_display(Membership.NONE)
+
+ return {'membership_level': new_level}
@csrf.csrf_exempt
@@ -1022,8 +1102,8 @@ def save_post_reject_reason(request):
@decorators.admins_only
def moderate_suggested_tag(request):
"""accepts or rejects a suggested tag
- if thread id is given, then tag is
- applied to or removed from only one thread,
+ if thread id is given, then tag is
+ applied to or removed from only one thread,
otherwise the decision applies to all threads
"""
form = forms.ModerateTagForm(request.POST)
@@ -1032,7 +1112,7 @@ def moderate_suggested_tag(request):
thread_id = form.cleaned_data.get('thread_id', None)
try:
- tag = models.Tag.objects.get(id = tag_id)#can tag not exist?
+ tag = models.Tag.objects.get(id=tag_id)#can tag not exist?
except models.Tag.DoesNotExist:
return
@@ -1170,7 +1250,7 @@ def share_question_with_group(request):
if group_name == askbot_settings.GLOBAL_GROUP_NAME:
thread.make_public(recursive=True)
else:
- group = models.Tag.group_tags.get(name=group_name)
+ group = models.Group.objects.get(name=group_name)
thread.add_to_groups((group,), recursive=True)
#get notif sets after
@@ -1230,3 +1310,96 @@ def share_question_with_user(request):
error_message = _('Sorry, looks like sharing request was invalid')
request.user.message_set.create(message=error_message)
return HttpResponseRedirect(thread.get_absolute_url())
+
+@csrf.csrf_protect
+def moderate_group_join_request(request):
+ """moderator of the group can accept or reject a new user"""
+ request_id = IntegerField().clean(request.POST['request_id'])
+ action = request.POST['action']
+ assert(action in ('approve', 'deny'))
+
+ activity = get_object_or_404(models.Activity, pk=request_id)
+ group = activity.content_object
+ applicant = activity.user
+
+ if group.has_moderator(request.user):
+ group_membership = models.GroupMembership.objects.get(
+ user=applicant, group=group
+ )
+ if action == 'approve':
+ group_membership.level = models.GroupMembership.FULL
+ group_membership.save()
+ msg_data = {'user': applicant.username, 'group': group.name}
+ message = _('%(user)s, welcome to group %(group)s!') % msg_data
+ applicant.message_set.create(message=message)
+ else:
+ group_membership.delete()
+
+ activity.delete()
+ url = request.user.get_absolute_url() + '?sort=inbox&section=join_requests'
+ return HttpResponseRedirect(url)
+ else:
+ raise Http404
+
+@decorators.get_only
+def get_editor(request):
+ """returns bits of html for the tinymce editor in a dictionary with keys:
+ * html - the editor element
+ * scripts - an array of script tags
+ * success - True
+ """
+ config = simplejson.loads(request.GET['config'])
+ form = forms.EditorForm(editor_attrs=config)
+ editor_html = render_text_into_skin(
+ '{{ form.media }} {{ form.editor }}',
+ {'form': form},
+ request
+ )
+ #parse out javascript and dom, and return them separately
+ #we need that, because js needs to be added in a special way
+ html_soup = BeautifulSoup(editor_html)
+
+ parsed_scripts = list()
+ for script in html_soup.find_all('script'):
+ parsed_scripts.append({
+ 'contents': script.string,
+ 'src': script.get('src', None)
+ })
+
+ data = {
+ 'html': str(html_soup.textarea),
+ 'scripts': parsed_scripts,
+ 'success': True
+ }
+ return HttpResponse(simplejson.dumps(data), mimetype='application/json')
+
+@csrf.csrf_exempt
+@decorators.ajax_only
+@decorators.post_only
+def publish_answer(request):
+ """will publish or unpublish answer, if
+ current thread is moderated
+ """
+ denied_msg = _('Sorry, only thread moderators can use this function')
+ if request.user.is_authenticated():
+ if request.user.is_administrator_or_moderator() is False:
+ raise exceptions.PermissionDenied(denied_msg)
+ #todo: assert permission
+ answer_id = IntegerField().clean(request.POST['answer_id'])
+ answer = models.Post.objects.get(id=answer_id, post_type='answer')
+
+ if answer.thread.has_moderator(request.user) is False:
+ raise exceptions.PermissionDenied(denied_msg)
+
+ enquirer = answer.thread._question_post().author
+ enquirer_group = enquirer.get_personal_group()
+
+ if answer.has_group(enquirer_group):
+ message = _('The answer is now unpublished')
+ answer.remove_from_groups([enquirer_group])
+ else:
+ answer.add_to_groups([enquirer_group])
+ message = _('The answer is now published')
+ #todo: notify enquirer by email about the post
+ request.user.message_set.create(message=message)
+ return {'redirect_url': answer.get_absolute_url()}
diff --git a/askbot/views/context.py b/askbot/views/context.py
index 12b77bd9..eeaf6002 100644
--- a/askbot/views/context.py
+++ b/askbot/views/context.py
@@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _
from askbot.conf import settings as askbot_settings
from askbot import const
from askbot.const import message_keys as msg
+from askbot.models import GroupMembership
def get_for_tag_editor():
#data for the tag editor
@@ -20,3 +21,32 @@ def get_for_tag_editor():
}
}
return {'tag_editor_settings': simplejson.dumps(data)}
+
+def get_for_inbox(user):
+ """adds response counts of various types"""
+ if user.is_anonymous():
+ return None
+
+ #get flags count
+ flag_activity_types = (const.TYPE_ACTIVITY_MARK_OFFENSIVE,)
+ if askbot_settings.ENABLE_CONTENT_MODERATION:
+ flag_activity_types += (
+ const.TYPE_ACTIVITY_MODERATED_NEW_POST,
+ const.TYPE_ACTIVITY_MODERATED_POST_EDIT
+ )
+
+ #get group_join_requests_count
+ group_join_requests_count = 0
+ if user.is_administrator_or_moderator():
+ pending_memberships = GroupMembership.objects.filter(
+ group__in=user.get_groups(),
+ level=GroupMembership.PENDING
+ )
+ group_join_requests_count = pending_memberships.count()
+
+ return {
+ 're_count': user.new_response_count + user.seen_response_count,
+ 'flags_count': user.get_notifications(flag_activity_types).count(),
+ 'group_join_requests_count': group_join_requests_count
+ }
+
diff --git a/askbot/views/meta.py b/askbot/views/meta.py
index e4209185..7b271219 100644
--- a/askbot/views/meta.py
+++ b/askbot/views/meta.py
@@ -16,6 +16,8 @@ from django.db.models import Max, Count
from askbot import skins
from askbot.conf import settings as askbot_settings
from askbot.forms import FeedbackForm
+from askbot.utils.url_utils import get_login_url
+from askbot.utils.forms import get_next_url
from askbot.mail import mail_moderators
from askbot.models import BadgeData, Award, User, Tag
from askbot.models import badges as badge_data
@@ -84,9 +86,19 @@ def faq(request):
def feedback(request):
data = {'page_class': 'meta'}
form = None
+
+ if askbot_settings.ALLOW_ANONYMOUS_FEEDBACK is False:
+ if request.user.is_anonymous():
+ message = _('Please sign in or register to send your feedback')
+ request.user.message_set.create(message=message)
+ redirect_url = get_login_url() + '?next=' + request.path
+ return HttpResponseRedirect(redirect_url)
+
if request.method == "POST":
- form = FeedbackForm(is_auth = request.user.is_authenticated(),
- data = request.POST)
+ form = FeedbackForm(
+ is_auth=request.user.is_authenticated(),
+ data=request.POST
+ )
if form.is_valid():
if not request.user.is_authenticated():
data['email'] = form.cleaned_data.get('email',None)
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 8d63ba51..c2391cab 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -424,7 +424,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
return HttpResponseRedirect(reverse('index'))
elif show_answer:
- #if the url calls to view a particular answer to
+ #if the url calls to view a particular answer to
#question - we must check whether the question exists
#whether answer is actually corresponding to the current question
#and that the visitor is allowed to see it
@@ -444,7 +444,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
#load answers and post id's->athor_id mapping
#posts are pre-stuffed with the correctly ordered comments
- updated_question_post, answers, post_to_author = thread.get_cached_post_data(
+ updated_question_post, answers, post_to_author, published_answer_ids = thread.get_cached_post_data(
sort_method = answer_sort_method,
user = request.user
)
@@ -572,6 +572,9 @@ def question(request, id):#refactor - long subroutine. display question body, an
'active_tab': 'questions',
'question' : question_post,
'thread': thread,
+ 'thread_is_moderated': thread.is_moderated(),
+ 'user_is_thread_moderator': thread.has_moderator(request.user),
+ 'published_answer_ids': published_answer_ids,
'answer' : answer_form,
'answers' : page_objects.object_list,
'answer_count': thread.get_answer_count(request.user),
@@ -590,6 +593,9 @@ def question(request, id):#refactor - long subroutine. display question body, an
'show_comment': show_comment,
'show_comment_position': show_comment_position,
}
+ #shared with ...
+ if askbot_settings.GROUPS_ENABLED:
+ data['sharing_info'] = thread.get_sharing_info()
data.update(context.get_for_tag_editor())
@@ -631,20 +637,3 @@ def get_comment(request):
comment = models.Post.objects.get(post_type='comment', id=id)
request.user.assert_can_edit_comment(comment)
return {'text': comment.text}
-
-def widget_questions(request):
- """Returns the first x questions based on certain tags.
- @returns template with those questions listed."""
- # make sure this is a GET request with the correct parameters.
- if request.method != 'GET':
- raise Http404
- threads = models.Thread.objects.all()
- tags_input = request.GET.get('tags','').strip()
- if len(tags_input) > 0:
- tags = [tag.strip() for tag in tags_input.split(',')]
- threads = threads.filter(tags__name__in=tags)
- data = {
- 'threads': threads[:askbot_settings.QUESTIONS_WIDGET_MAX_QUESTIONS]
- }
- return render_into_skin('question_widget.html', data, request)
-
diff --git a/askbot/views/users.py b/askbot/views/users.py
index 88907b46..b0b0cfbe 100644
--- a/askbot/views/users.py
+++ b/askbot/views/users.py
@@ -33,6 +33,7 @@ from askbot.utils.http import get_request_info
from askbot.utils import functions
from askbot import forms
from askbot import const
+from askbot.views import context as view_context
from askbot.conf import settings as askbot_settings
from askbot import models
from askbot import exceptions
@@ -41,7 +42,6 @@ from askbot.models.tag import get_global_group
from askbot.models.tag import get_groups
from askbot.models.tag import format_personal_group_name
from askbot.skins.loaders import render_into_skin
-from askbot.templatetags import extra_tags
from askbot.search.state_manager import SearchState
from askbot.utils import url_utils
from askbot.utils.loading import load_module
@@ -61,11 +61,20 @@ def owner_or_moderator_required(f):
def show_users(request, by_group=False, group_id=None, group_slug=None):
"""Users view, including listing of users by group"""
+
+ if askbot_settings.GROUPS_ENABLED and not by_group:
+ default_group = get_global_group()
+ group_slug = slugify(default_group.name)
+ new_url = reverse('users_by_group',
+ kwargs={'group_id': default_group.id,
+ 'group_slug': group_slug})
+ return HttpResponseRedirect(new_url)
+
users = models.User.objects.exclude(status = 'b')
group = None
group_email_moderation_enabled = False
- user_can_join_group = False
- user_is_group_member = False
+ user_acceptance_level = 'closed'
+ user_membership_level = 'none'
if by_group == True:
if askbot_settings.GROUPS_ENABLED == False:
raise Http404
@@ -74,25 +83,31 @@ def show_users(request, by_group=False, group_id=None, group_slug=None):
return HttpResponseRedirect('groups')
else:
try:
- group = models.Tag.group_tags.get(id = group_id)
+ group = models.Group.objects.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:
+ user_acceptance_level = group.get_openness_level_for_user(
+ request.user
+ )
+ except models.Group.DoesNotExist:
raise Http404
if group_slug == slugify(group.name):
- users = users.filter(
- group_memberships__group__id = group_id
- )
- if request.user.is_authenticated():
- user_is_group_member = bool(
- users.filter(
- id = request.user.id
- ).count()
+ #filter users by full group memberships
+ #todo: refactor as Group.get_full_members()
+ full_level = models.GroupMembership.FULL
+ memberships = models.GroupMembership.objects.filter(
+ group=group, level=full_level
)
+ user_ids = memberships.values_list('user__id', flat=True)
+ users = users.filter(id__in=user_ids)
+ if request.user.is_authenticated():
+ membership = request.user.get_group_membership(group)
+ if membership:
+ user_membership_level = membership.get_level_display()
+
else:
group_page_url = reverse(
'users_by_group',
@@ -157,14 +172,18 @@ def show_users(request, by_group=False, group_id=None, group_slug=None):
}
paginator_context = functions.setup_paginator(paginator_data) #
+ #todo: move to contexts
+ #extra context for the groups
if askbot_settings.GROUPS_ENABLED:
#todo: cleanup this branched code after groups are migrated to auth_group
user_groups = get_groups().exclude(name__startswith='_internal_')
if len(user_groups) <= 1:
assert(user_groups[0].name == askbot_settings.GLOBAL_GROUP_NAME)
user_groups = None
+ group_openness_choices = models.Group().get_openness_choices()
else:
user_groups = None
+ group_openness_choices = None
data = {
'active_tab': 'users',
@@ -175,10 +194,12 @@ def show_users(request, by_group=False, group_id=None, group_slug=None):
'tab_id' : sortby,
'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,
- 'user_groups': user_groups
+ 'user_acceptance_level': user_acceptance_level,
+ 'user_membership_level': user_membership_level,
+ 'user_groups': user_groups,
+ 'group_openness_choices': group_openness_choices
}
+
return render_into_skin('users.html', data, request)
@csrf.csrf_protect
@@ -404,7 +425,7 @@ def user_stats(request, user, context):
interesting_tag_names = None
ignored_tag_names = None
subscribed_tag_names = None
-
+
# tags = models.Post.objects.filter(author=user).values('id', 'thread', 'thread__tags')
# post_ids = set()
# thread_ids = set()
@@ -460,7 +481,7 @@ def user_stats(request, user, context):
badges = badges_dict.items()
badges.sort(key=operator.itemgetter(1), reverse=True)
- user_groups = models.Tag.group_tags.get_for_user(user = user)
+ user_groups = models.Group.objects.get_for_user(user = user)
user_groups = user_groups.exclude(name__startswith='_internal_')
global_group = get_global_group()
user_groups = user_groups.exclude(name=global_group.name)
@@ -657,6 +678,39 @@ def user_recent(request, user, context):
context.update(data)
return render_into_skin('user_profile/user_recent.html', context, request)
+#not a view - no direct url route here, called by `user_responses`
+@csrf.csrf_protect
+def show_group_join_requests(request, user, context):
+ """show group join requests to admins who belong to the group"""
+ if request.user.is_administrator_or_moderator() is False:
+ raise Http404
+
+ #get group to which user belongs
+ groups = request.user.get_groups()
+ #construct a dictionary group id --> group object
+ #to avoid loading group via activity content object
+ groups_dict = dict([(group.id, group) for group in groups])
+
+ #get join requests for those groups
+ group_content_type = ContentType.objects.get_for_model(models.Group)
+ join_requests = models.Activity.objects.filter(
+ activity_type=const.TYPE_ACTIVITY_ASK_TO_JOIN_GROUP,
+ content_type=group_content_type,
+ object_id__in=groups_dict.keys()
+ ).order_by('-active_at')
+ data = {
+ 'active_tab':'users',
+ 'page_class': 'user-profile-page',
+ 'tab_name' : 'join_requests',
+ 'tab_description' : _('group joining requests'),
+ 'page_title' : _('profile - moderation'),
+ 'groups_dict': groups_dict,
+ 'join_requests': join_requests
+ }
+ context.update(data)
+ return render_into_skin('user_inbox/group_join_requests.html', context, request)
+
+
@owner_or_moderator_required
def user_responses(request, user, context):
"""
@@ -670,6 +724,10 @@ def user_responses(request, user, context):
and "flags" - moderation items for mods only
"""
+ #0) temporary, till urls are fixed: update context
+ # to contain response counts for all sub-sections
+ context.update(view_context.get_for_inbox(request.user))
+
#1) select activity types according to section
section = request.GET.get('section', 'forum')
if section == 'flags' and not\
@@ -686,15 +744,15 @@ def user_responses(request, user, context):
const.TYPE_ACTIVITY_MODERATED_NEW_POST,
const.TYPE_ACTIVITY_MODERATED_POST_EDIT
)
+ elif section == 'join_requests':
+ return show_group_join_requests(request, user, context)
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
- ).select_related(
+ memo_set = request.user.get_notifications(activity_types)
+ memo_set = memo_set.select_related(
'activity',
'activity__content_type',
'activity__question__thread',
@@ -748,14 +806,14 @@ def user_responses(request, user, context):
'active_tab':'users',
'page_class': 'user-profile-page',
'tab_name' : 'inbox',
- 'inbox_section':section,
+ 'inbox_section': section,
'tab_description' : _('comments and answers to others questions'),
'page_title' : _('profile - responses'),
'post_reject_reasons': reject_reasons,
'responses' : filtered_response_list,
}
context.update(data)
- return render_into_skin('user_profile/user_inbox.html', context, request)
+ return render_into_skin('user_inbox/responses_and_flags.html', context, request)
def user_network(request, user, context):
if 'followit' not in django_settings.INSTALLED_APPS:
@@ -1009,15 +1067,14 @@ def groups(request, id = None, slug = None):
scope = 'all-groups'
if scope == 'all-groups':
- groups = models.Tag.group_tags.get_all()
+ groups = models.Group.objects.all()
else:
- groups = models.Tag.group_tags.get_for_user(
- user = request.user
+ groups = models.Group.objects.get_for_user(
+ user=request.user
)
groups = groups.exclude(name__startswith='_internal_')
-
- groups = groups.select_related('group_profile')
+ groups = groups.annotate(users_count=Count('user'))
user_can_add_groups = request.user.is_authenticated() and \
request.user.is_administrator_or_moderator()
diff --git a/askbot/views/widgets.py b/askbot/views/widgets.py
index 77eb7eb5..8699cdf1 100644
--- a/askbot/views/widgets.py
+++ b/askbot/views/widgets.py
@@ -1,48 +1,106 @@
from datetime import datetime
from django.core import exceptions
-from django.utils import simplejson
-from django.shortcuts import redirect
+from django.template import Context
+from django.http import HttpResponse, Http404
from django.views.decorators import csrf
-from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
+from django.shortcuts import redirect, get_object_or_404
+from django.views.decorators.cache import cache_page
from django.contrib.auth.decorators import login_required
-from askbot.skins.loaders import render_into_skin
+from askbot.skins.loaders import render_into_skin, get_template
+from askbot.conf import settings as askbot_settings
+from askbot.utils import decorators
from askbot import models
from askbot import forms
+WIDGETS_MODELS = {
+ 'ask': models.AskWidget,
+ 'question': models.QuestionWidget
+ }
+
+WIDGETS_FORMS = {
+ 'ask': forms.CreateAskWidgetForm,
+ 'question': forms.CreateQuestionWidgetForm,
+ }
+
+def _get_model(key):
+ '''like get_object_or_404 but for our models'''
+ try:
+ return WIDGETS_MODELS[key]
+ except KeyError:
+ raise Http404
+
+def _get_form(key):
+ '''like get_object_or_404 but for our forms'''
+ try:
+ return WIDGETS_FORMS[key]
+ except KeyError:
+ raise Http404
+
+@decorators.admins_only
+def widgets(request):
+ data = {
+ 'ask_widgets': models.AskWidget.objects.all().count(),
+ 'question_widgets': models.QuestionWidget.objects.all().count(),
+ 'page_class': 'widgets'
+ }
+ return render_into_skin('embed/widgets.html', data, request)
+
@csrf.csrf_protect
-def ask_widget(request):
+def ask_widget(request, widget_id):
def post_question(data, request):
- thread = models.Thread.objects.create_new(**data_dict)
+ thread = models.Thread.objects.create_new(**data)
question = thread._question_post()
request.session['widget_question_url'] = question.get_absolute_url()
return question
+ widget = get_object_or_404(models.AskWidget, id=widget_id)
if request.method == "POST":
- form = forms.AskWidgetForm(request.POST)
+ form = forms.AskWidgetForm(include_text=widget.include_text_field,
+ data=request.POST)
if form.is_valid():
ask_anonymously = form.cleaned_data['ask_anonymously']
title = form.cleaned_data['title']
+ if widget.include_text_field:
+ text = form.cleaned_data['text']
+ else:
+ text = ' '
+
+
+ if widget.group:
+ group_id = widget.group.id
+ else:
+ group_id = None
+
+ if widget.tag:
+ tagnames = widget.tag.name
+ else:
+ tagnames = ''
+
data_dict = {
- 'title': title,
- 'added_at': datetime.now(),
- 'wiki': False,
- 'text': ' ',
- 'tagnames': '',
- 'is_anonymous': ask_anonymously
- }
+ 'title': title,
+ 'added_at': datetime.now(),
+ 'wiki': False,
+ 'text': text,
+ 'tagnames': tagnames,
+ 'group_id': group_id,
+ 'is_anonymous': ask_anonymously
+ }
if request.user.is_authenticated():
data_dict['author'] = request.user
question = post_question(data_dict, request)
return redirect('ask_by_widget_complete')
else:
request.session['widget_question'] = data_dict
- next_url = '%s?next=%s' % (reverse('widget_signin'), reverse('ask_by_widget'))
+ next_url = '%s?next=%s' % (
+ reverse('widget_signin'),
+ reverse('ask_by_widget', args=(widget.id,))
+ )
return redirect(next_url)
else:
if 'widget_question' in request.session and \
@@ -58,17 +116,159 @@ def ask_widget(request):
next_url = '%s?next=%s' % (reverse('widget_signin'), reverse('ask_by_widget'))
return redirect(next_url)
- form = forms.AskWidgetForm()
- data = {'form': form}
- return render_into_skin('ask_by_widget.html', data, request)
+ form = forms.AskWidgetForm(include_text=widget.include_text_field)
+
+ data = {
+ 'form': form,
+ 'widget': widget,
+ 'editor_type': askbot_settings.EDITOR_TYPE
+ }
+ return render_into_skin('embed/ask_by_widget.html', data, request)
@login_required
def ask_widget_complete(request):
question_url = request.session.get('widget_question_url')
+ custom_css = request.session.get('widget_css')
if question_url:
del request.session['widget_question_url']
else:
question_url = '#'
- data = {'question_url': question_url}
- return render_into_skin('ask_widget_complete.html', data, request)
+ if custom_css:
+ del request.session['widget_css']
+
+ data = {'question_url': question_url, 'custom_css': custom_css}
+ return render_into_skin('embed/ask_widget_complete.html', data, request)
+
+
+@decorators.admins_only
+def list_widgets(request, model):
+ model_class = _get_model(model)
+ widgets = model_class.objects.all()
+ data = {
+ 'widgets': widgets,
+ 'widget_name': model
+ }
+ return render_into_skin('embed/list_widgets.html', data, request)
+
+@decorators.admins_only
+def create_widget(request, model):
+ form_class = _get_form(model)
+ model_class = _get_model(model)
+ if request.method == 'POST':
+ form = form_class(request.POST)
+ if form.is_valid():
+ instance = model_class(**form.cleaned_data)
+ instance.save()
+ return redirect('list_widgets', model=model)
+ else:
+ form = form_class()
+
+ data = {'form': form,
+ 'action': 'edit',
+ 'widget_name': model}
+ return render_into_skin('embed/widget_form.html', data, request)
+
+@decorators.admins_only
+def edit_widget(request, model, widget_id):
+ model_class = _get_model(model)
+ form_class = _get_form(model)
+ widget = get_object_or_404(model_class, pk=widget_id)
+ if request.method == 'POST':
+ form = form_class(request.POST)
+ if form.is_valid():
+ form_dict = dict.copy(form.cleaned_data)
+ for key in widget.__dict__:
+ if key.endswith('_id'):
+ form_key = key.split('_id')[0]
+ if form_dict[form_key]:
+ form_dict[key] = form_dict[form_key].id
+ del form_dict[form_key]
+ else:
+ continue
+
+ widget.__dict__.update(form_dict)
+ widget.save()
+ return redirect('list_widgets', model=model)
+ else:
+ initial_dict = dict.copy(widget.__dict__)
+ for key in initial_dict:
+ if key.endswith('_id'):
+ new_key = key.split('_id')[0]
+ initial_dict[new_key] = initial_dict[key]
+ del initial_dict[key]
+ else:
+ continue
+
+ del initial_dict['_state']
+ form = form_class(initial=initial_dict)
+
+ data = {'form': form,
+ 'action': 'edit',
+ 'widget_name': model}
+ return render_into_skin('embed/widget_form.html', data, request)
+
+@decorators.admins_only
+def delete_widget(request, model, widget_id):
+ model_class = _get_model(model)
+ widget = get_object_or_404(model_class, pk=widget_id)
+ if request.method == "POST":
+ widget.delete()
+ return redirect('list_widgets', model=model)
+ else:
+ return render_into_skin('embed/delete_widget.html',
+ {'widget': widget, 'widget_name': model}, request)
+
+def render_ask_widget_js(request, widget_id):
+ widget = get_object_or_404(models.AskWidget, pk=widget_id)
+ variable_name = "AskbotAskWidget%d" % widget.id
+ content_tpl = get_template('embed/askbot_widget.js', request)
+ context_dict = {'widget': widget,
+ 'host': request.get_host(),
+ 'variable_name': variable_name}
+ content = content_tpl.render(Context(context_dict))
+ return HttpResponse(content, mimetype='text/javascript')
+
+def render_ask_widget_css(request, widget_id):
+ widget = get_object_or_404(models.AskWidget, pk=widget_id)
+ variable_name = "AskbotAskWidget%d" % widget.id
+ content_tpl = get_template('embed/askbot_widget.css', request)
+ context_dict = {'widget': widget,
+ 'host': request.get_host(),
+ 'editor_type': askbot_settings.EDITOR_TYPE,
+ 'variable_name': variable_name}
+ content = content_tpl.render(Context(context_dict))
+ return HttpResponse(content, mimetype='text/css')
+
+def question_widget(request, widget_id):
+ """Returns the first x questions based on certain tags.
+ @returns template with those questions listed."""
+ # make sure this is a GET request with the correct parameters.
+ widget = get_object_or_404(models.QuestionWidget, pk=widget_id)
+
+ if request.method != 'GET':
+ raise Http404
+
+ filter_params = {}
+
+ if widget.tagnames:
+ filter_params['tags__name__in'] = widget.tagnames.split(' ')
+
+ if widget.group:
+ filter_params['groups'] = widget.group
+
+ #simple title search for now
+ if widget.search_query:
+ filter_params['title__icontains'] = widget.search_query
+
+ if filter_params:
+ threads = models.Thread.objects.filter(**filter_params).order_by(widget.order_by)[:widget.question_number]
+ else:
+ threads = models.Thread.objects.all().order_by(widget.order_by)[:widget.question_number]
+
+ data = {
+ 'threads': threads,
+ 'widget': widget
+ }
+
+ return render_into_skin('embed/question_widget.html', data, request)
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index a9f1c4a0..85a92d23 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -50,7 +50,7 @@ QUESTIONS_PAGE_SIZE = 10
ANSWERS_PAGE_SIZE = 10
@csrf.csrf_exempt
-def upload(request):#ajax upload file to a question or answer
+def upload(request):#ajax upload file to a question or answer
"""view that handles file upload via Ajax
"""
@@ -71,6 +71,7 @@ def upload(request):#ajax upload file to a question or answer
if file_name_prefix not in ('', 'group_logo_'):
raise exceptions.PermissionDenied('invalid upload file name prefix')
+ #todo: check file type
f = request.FILES['file-upload']#take first file
#todo: extension checking should be replaced with mimetype checking
#and this must be part of the form validation
@@ -121,14 +122,14 @@ def __import_se_data(dump_file):
"""non-view function that imports the SE data
in the future may import other formats as well
- In this function stdout is temporarily
+ In this function stdout is temporarily
redirected, so that the underlying importer management
command could stream the output to the browser
todo: maybe need to add try/except clauses to restore
the redirects in the exceptional situations
"""
-
+
fake_stdout = tempfile.NamedTemporaryFile()
real_stdout = sys.stdout
sys.stdout = fake_stdout
@@ -226,7 +227,7 @@ def ask(request):#view used to ask a new question
author=request.user
)
drafts.delete()
-
+
user = form.get_post_user(request.user)
try:
question = user.post_question(
@@ -421,7 +422,7 @@ def edit_question(request, id):
body_text = form.cleaned_data['text'],
revision_comment = form.cleaned_data['summary'],
tags = form.cleaned_data['tags'],
- wiki = is_wiki,
+ wiki = is_wiki,
edit_anonymously = is_anon_edit,
is_private = post_privately
)
@@ -720,3 +721,68 @@ def delete_comment(request):
unicode(e),
mimetype = 'application/json'
)
+
+@decorators.admins_only
+@decorators.post_only
+def comment_to_answer(request):
+ comment_id = request.POST.get('comment_id')
+ if comment_id:
+ comment_id = int(comment_id)
+ comment = get_object_or_404(models.Post,
+ post_type='comment', id=comment_id)
+ comment.post_type = 'answer'
+ old_parent = comment.parent
+
+ comment.parent = comment.thread._question_post()
+ comment.save()
+
+ comment.thread.update_answer_count()
+
+ comment.parent.comment_count += 1
+ comment.parent.save()
+
+ #to avoid db constraint error
+ if old_parent.comment_count >= 1:
+ old_parent.comment_count -= 1
+ else:
+ old_parent.comment_count = 0
+
+ old_parent.save()
+
+ comment.thread.invalidate_cached_data()
+
+ return HttpResponseRedirect(comment.get_absolute_url())
+ else:
+ raise Http404
+
+@decorators.admins_only
+@decorators.post_only
+def answer_to_comment(request):
+ answer_id = request.POST.get('answer_id')
+ if answer_id:
+ answer_id = int(answer_id)
+ answer = get_object_or_404(models.Post,
+ post_type = 'answer', id=answer_id)
+ if len(answer.text) <= 300:
+ answer.post_type = 'comment'
+ answer.parent = answer.thread._question_post()
+ #can we trust this?
+ old_comment_count = answer.comment_count
+ answer.comment_count = 0
+
+ answer_comments = models.Post.objects.get_comments().filter(parent=answer)
+ answer_comments.update(parent=answer.parent)
+
+ answer.parse_and_save(author=answer.author)
+ answer.thread.update_answer_count()
+
+ answer.parent.comment_count = 1 + old_comment_count
+ answer.parent.save()
+
+ answer.thread.invalidate_cached_data()
+ else:
+ request.user.message_set.create(message = _("the selected answer cannot be a comment"))
+
+ return HttpResponseRedirect(answer.get_absolute_url())
+ else:
+ raise Http404
diff --git a/askbot_requirements.txt b/askbot_requirements.txt
index 8771a7e6..2be12b8e 100644
--- a/askbot_requirements.txt
+++ b/askbot_requirements.txt
@@ -21,3 +21,4 @@ pystache==0.3.1
pytz
django-tinymce
longerusername
+beautifulsoup4