From: Attila-Mihaly Balazs Date: Tue, 3 Dec 2013 13:53:08 +0000 (+0200) Subject: Migrate to Django 1.6 X-Git-Tag: live~28^2~6 X-Git-Url: https://git.openstreetmap.org/osqa.git/commitdiff_plain/dcacc3b4c2ba2f550806a2d22b183f6fac4f63ff Migrate to Django 1.6 --- diff --git a/forum/actions/node.py b/forum/actions/node.py index 5ba6e2b..f9f2206 100644 --- a/forum/actions/node.py +++ b/forum/actions/node.py @@ -4,6 +4,8 @@ from forum.models.action import ActionProxy from forum.models import Comment, Question, Answer, NodeRevision import logging +from django.contrib import messages + class NodeEditAction(ActionProxy): def create_revision_data(self, initial=False, **data): revision_data = dict(summary=data.get('summary', (initial and _('Initial revision') or '')), body=data['text']) @@ -28,7 +30,7 @@ class AskAction(NodeEditAction): question.save() self.node = question - self.user.message_set.create(message=self.describe(self.user)) + messages.info(request, self.describe(self.user)) def describe(self, viewer=None): return _("%(user)s asked %(question)s") % { @@ -47,7 +49,7 @@ class AnswerAction(NodeEditAction): def process_action(self): self.node.question.reset_answer_count_cache() - self.user.message_set.create(message=self.describe(self.user)) + messages.info(request, self.describe(self.user)) def describe(self, viewer=None): diff --git a/forum/actions/user.py b/forum/actions/user.py index 7d636be..3e1721b 100644 --- a/forum/actions/user.py +++ b/forum/actions/user.py @@ -7,6 +7,8 @@ from forum import settings from forum.settings import APP_SHORT_NAME from forum.utils.mail import send_template_email +from django.contrib import messages + class UserJoinsAction(ActionProxy): verb = _("joined") @@ -76,8 +78,7 @@ class BonusRepAction(ActionProxy): message=_("Congratulations, you have been awarded an extra %s reputation points.") % self._value + '
%s' % self.extra.get('message', _('Thank you'))) else: - self._affected.message_set.create( - message=_("You have been penalized in %s reputation points.") % self._value + + messages.info(request, _("You have penalized %s in %s reputation points.") % (self._affected, self._value) + '
%s' % self.extra.get('message', '')) def describe(self, viewer=None): @@ -110,7 +111,6 @@ class AwardPointsAction(ActionProxy): self.repute(self._affected, self._value) self.repute(self.user, -self._value) - self._affected.message_set.create( message=_("Congratulations, you have been awarded an extra %(points)s reputation %(points_label)s on this answer.") % { 'points': self._value, diff --git a/forum/context.py b/forum/context.py index 641af38..957b5f9 100644 --- a/forum/context.py +++ b/forum/context.py @@ -6,18 +6,12 @@ def application_settings(context): def auth_processor(request): if hasattr(request, 'user'): user = request.user - if user.is_authenticated(): - messages = user.message_set.all() - else: - messages = None else: from django.contrib.auth.models import AnonymousUser user = AnonymousUser() - messages = None from django.core.context_processors import PermWrapper return { 'user': user, - 'messages': messages, 'perms': PermWrapper(user), } diff --git a/forum/forms/general.py b/forum/forms/general.py index 01c528b..06ee23c 100644 --- a/forum/forms/general.py +++ b/forum/forms/general.py @@ -3,7 +3,6 @@ import re from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe from forum import settings -from django.http import str_to_unicode from forum.models import User from forum.modules import call_all_handlers import urllib @@ -13,7 +12,9 @@ DEFAULT_NEXT = getattr(settings, 'APP_BASE_URL') def clean_next(next): if next is None: return DEFAULT_NEXT - next = str_to_unicode(urllib.unquote(next), 'utf-8') + next = urllib.unquote(next) + if not isinstance(next, unicode): + next = unicode(next, 'utf-8') next = next.strip() if next.startswith('/'): return next diff --git a/forum/forms/qanda.py b/forum/forms/qanda.py index d2cc837..ea75ddd 100644 --- a/forum/forms/qanda.py +++ b/forum/forms/qanda.py @@ -354,6 +354,7 @@ class SubscriptionSettingsForm(forms.ModelForm): class Meta: model = SubscriptionSettings + fields = ['enable_notifications', 'member_joins', 'new_question', 'new_question_watched_tags', 'subscribed_questions'] class UserPreferencesForm(forms.Form): sticky_sorts = forms.BooleanField(required=False, initial=False) diff --git a/forum/middleware/admin_messages.py b/forum/middleware/admin_messages.py index 2d391de..71af55f 100644 --- a/forum/middleware/admin_messages.py +++ b/forum/middleware/admin_messages.py @@ -1,7 +1,7 @@ -from forum.user_messages import create_message from django.utils.translation import ugettext as _ from django.core.urlresolvers import reverse from django.core.exceptions import ObjectDoesNotExist +from django.contrib import messages from forum.settings import EMAIL_HOST, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, \ APP_URL @@ -29,14 +29,8 @@ class AdminMessagesMiddleware(object): # We do not want to repeat ourselves. If the message already exists in the message list, we're not going to # add it. That's why first of all we're going the check if it is there. - try: - # If the message doesn't exist in the RelatedManager ObjectsDoesNotExist is going to be raised. - request.user.message_set.all().get(message=msg) - except ObjectDoesNotExist: - # Let's create the message. - request.user.message_set.create(message=msg) - except: - pass + if msg not in [m.message for m in messages.api.get_messages(request)]: + messages.info(request, msg) def check_app_url(self, request): # We consider the APP_URL setting not configured if it contains only the protocol @@ -50,11 +44,5 @@ class AdminMessagesMiddleware(object): # We do not want to repeat ourselves. If the message already exists in the message list, we're not going to # add it. That's why first of all we're going the check if it is there. - try: - # If the message doesn't exist in the RelatedManager ObjectsDoesNotExist is going to be raised. - request.user.message_set.all().get(message=msg) - except ObjectDoesNotExist: - # Let's create the message. - request.user.message_set.create(message=msg) - except: - pass + if msg not in [m.message for m in messages.api.get_messages(request)]: + messages.info(request, msg) diff --git a/forum/middleware/anon_user.py b/forum/middleware/anon_user.py index 25f192a..dafca3e 100644 --- a/forum/middleware/anon_user.py +++ b/forum/middleware/anon_user.py @@ -1,11 +1,12 @@ from django.http import HttpResponseRedirect, HttpResponse from forum.forms import get_next_url from django.utils.translation import ugettext as _ -from forum.user_messages import create_message, get_and_delete_messages from forum import settings from django.core.urlresolvers import reverse import logging +from django.contrib import messages + class AnonymousMessageManager(object): def __init__(self,request): self.request = request @@ -25,8 +26,6 @@ class ConnectToSessionMessagesMiddleware(object): def process_request(self, request): if not request.user.is_authenticated(): request.user.__deepcopy__ = dummy_deepcopy #plug on deepcopy which may be called by django db "driver" - request.user.message_set = AnonymousMessageManager(request) #here request is linked to anon user - request.user.get_and_delete_messages = request.user.message_set.get_and_delete #also set the first greeting one time per session only if 'greeting_set' not in request.session: @@ -37,7 +36,7 @@ class ConnectToSessionMessagesMiddleware(object): # If the store greeting in cookie setting is activated make sure that the greeting_set cookies isn't set if (settings.STORE_GREETING_IN_COOKIE and not request.COOKIES.has_key('greeting_set')) or \ not settings.STORE_GREETING_IN_COOKIE: - request.user.message_set.create(message=msg) + messages.info(request, msg) if settings.STORE_GREETING_IN_COOKIE: request.COOKIES.set(key='greeting_set', value=True) diff --git a/forum/middleware/cancel.py b/forum/middleware/cancel.py index 2d78a17..9d8f870 100644 --- a/forum/middleware/cancel.py +++ b/forum/middleware/cancel.py @@ -1,6 +1,8 @@ from django.http import HttpResponseRedirect from forum.forms import get_next_url import logging +from django.contrib import messages + class CancelActionMiddleware(object): def process_view(self, request, view_func, view_args, view_kwargs): if 'cancel' in request.REQUEST: @@ -9,7 +11,7 @@ class CancelActionMiddleware(object): msg = getattr(view_func,'CANCEL_MESSAGE') except AttributeError: msg = 'action canceled' - request.user.message_set.create(message=msg) + messages.info(request, msg) return HttpResponseRedirect(get_next_url(request)) else: return None diff --git a/forum/models/action.py b/forum/models/action.py index 7e88745..b3cfd26 100644 --- a/forum/models/action.py +++ b/forum/models/action.py @@ -28,7 +28,7 @@ class ActionQuerySet(CachedQuerySet): class ActionManager(CachedManager): use_for_related_fields = True - def get_query_set(self): + def get_queryset(self): qs = ActionQuerySet(self.model) if self.model is not Action: diff --git a/forum/models/base.py b/forum/models/base.py index 912c21a..9c328c5 100644 --- a/forum/models/base.py +++ b/forum/models/base.py @@ -190,7 +190,7 @@ class CachedQuerySet(models.query.QuerySet): class CachedManager(models.Manager): use_for_related_fields = True - def get_query_set(self): + def get_queryset(self): return CachedQuerySet(self.model) def get_or_create(self, *args, **kwargs): @@ -226,7 +226,7 @@ class DenormalizedField(object): cls.add_to_class("reset_%s_cache" % name, reset_cache) -class BaseMetaClass(models.Model.__metaclass__): +class BaseMetaClass(type(models.Model)): to_denormalize = [] def __new__(cls, *args, **kwargs): diff --git a/forum/models/node.py b/forum/models/node.py index 5b6cdb5..20ede47 100644 --- a/forum/models/node.py +++ b/forum/models/node.py @@ -152,7 +152,7 @@ class NodeQuerySet(CachedQuerySet): class NodeManager(CachedManager): use_for_related_fields = True - def get_query_set(self): + def get_queryset(self): qs = NodeQuerySet(self.model) # If the node is an answer, question or comment we filter the Node model by type diff --git a/forum/models/tag.py b/forum/models/tag.py index 73b1e30..eca23e4 100644 --- a/forum/models/tag.py +++ b/forum/models/tag.py @@ -11,8 +11,8 @@ from forum import modules class ActiveTagManager(CachedManager): use_for_related_fields = True - def get_query_set(self): - return super(ActiveTagManager, self).get_query_set().exclude(used_count__lt=1) + def get_queryset(self): + return super(ActiveTagManager, self).get_queryset().exclude(used_count__lt=1) class Tag(BaseModel): name = models.CharField(max_length=255, unique=True) diff --git a/forum/models/user.py b/forum/models/user.py index cf47c66..0437614 100644 --- a/forum/models/user.py +++ b/forum/models/user.py @@ -209,15 +209,6 @@ class User(BaseModel, DjangoUser): sub_settings = SubscriptionSettings(user=self) sub_settings.save() - def get_messages(self): - messages = [] - for m in self.message_set.all(): - messages.append(m.message) - return messages - - def delete_messages(self): - self.message_set.all().delete() - @models.permalink def get_profile_url(self): keyword_arguments = { diff --git a/forum/models/utils.py b/forum/models/utils.py index 1fbda58..5a1912c 100644 --- a/forum/models/utils.py +++ b/forum/models/utils.py @@ -81,7 +81,7 @@ class PickledObjectField(models.Field): raise return value - def get_db_prep_value(self, value): + def get_db_prep_value(self, value, connection, prepared=False): if value is not None and not isinstance(value, PickledObject): if type(value).__name__ in self.markable_types and not (isinstance(value, basestring) and len(value ) > MAX_MARKABLE_STRING_LENGTH): diff --git a/forum/modules/__init__.py b/forum/modules/__init__.py index f1ad7d9..6942c91 100644 --- a/forum/modules/__init__.py +++ b/forum/modules/__init__.py @@ -16,15 +16,10 @@ def get_modules_script(script_name): try: all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__])) - except ImportError, e: - #print repr(type(e)) + m.__name__ + ":" + str(e) - pass - except: - import traceback - msg = "Error importing %s from module %s: \n %s" % ( - script_name, m, traceback.format_exc() - ) - logging.error(msg) + except Exception, e: + if isinstance(e, ImportError) and str(e).endswith(script_name): + continue + logging.exception("Error importing %s from module %s", script_name, m) return all diff --git a/forum/settings/__init__.py b/forum/settings/__init__.py index 218f114..1458599 100644 --- a/forum/settings/__init__.py +++ b/forum/settings/__init__.py @@ -4,17 +4,17 @@ from base import Setting, SettingSet, BaseSetting from django.forms.widgets import Textarea from django.utils.translation import ugettext_lazy as _ from django.conf import settings as djsettings -from django.utils.version import get_svn_revision +from django.utils.version import get_git_changeset from forum.modules import get_modules_script_implementations OSQA_VERSION = "Development Build" -SVN_REVISION = get_svn_revision(djsettings.SITE_SRC_ROOT) +VCS_REVISION = get_git_changeset() # We'll have to keep in mind this variable on every release. -if SVN_REVISION == u'SVN-unknown': - SVN_REVISION = u'SVN-1000' +if VCS_REVISION == u'SVN-unknown': + VCS_REVISION = u'SVN-1000' MAINTAINANCE_MODE = Setting('MAINTAINANCE_MODE', None) diff --git a/forum/settings/basic.py b/forum/settings/basic.py index a4efd6f..1f9ddc9 100644 --- a/forum/settings/basic.py +++ b/forum/settings/basic.py @@ -1,52 +1,52 @@ -import os.path - -from base import Setting, SettingSet -from forms import ImageFormWidget - -from django.utils.translation import ugettext_lazy as _ -from django.forms.widgets import Textarea - -BASIC_SET = SettingSet('basic', _('Basic settings'), _("The basic settings for your application"), 1) - -APP_LOGO = Setting('APP_LOGO', '/upfiles/logo.png', BASIC_SET, dict( -label = _("Application logo"), -help_text = _("Your site main logo."), -widget=ImageFormWidget)) - -APP_FAVICON = Setting('APP_FAVICON', '/m/default/media/images/favicon.ico', BASIC_SET, dict( -label = _("Favicon"), -help_text = _("Your site favicon."), -widget=ImageFormWidget)) - -APP_TITLE = Setting('APP_TITLE', u'OSQA: Open Source Q&A Forum', BASIC_SET, dict( -label = _("Application title"), -help_text = _("The title of your application that will show in the browsers title bar"))) - -APP_SHORT_NAME = Setting(u'APP_SHORT_NAME', 'OSQA', BASIC_SET, dict( -label = _("Application short name"), -help_text = "The short name for your application that will show up in many places.")) - -APP_KEYWORDS = Setting('APP_KEYWORDS', u'OSQA,CNPROG,forum,community', BASIC_SET, dict( -label = _("Application keywords"), -help_text = _("The meta keywords that will be available through the HTML meta tags."))) - -APP_DESCRIPTION = Setting('APP_DESCRIPTION', u'Ask and answer questions.', BASIC_SET, dict( -label = _("Application description"), -help_text = _("The description of your application"), -widget=Textarea)) - -APP_COPYRIGHT = Setting('APP_COPYRIGHT', u'Copyright OSQA, 2010. Some rights reserved under creative commons license.', BASIC_SET, dict( -label = _("Copyright notice"), -help_text = _("The copyright notice visible at the footer of your page."))) - -SUPPORT_URL = Setting('SUPPORT_URL', '', BASIC_SET, dict( -label = _("Support URL"), -help_text = _("The URL provided for users to get support. It can be http: or mailto: or whatever your preferred support scheme is."), -required=False)) - -CONTACT_URL = Setting('CONTACT_URL', '', BASIC_SET, dict( -label = _("Contact URL"), -help_text = _("The URL provided for users to contact you. It can be http: or mailto: or whatever your preferred contact scheme is."), -required=False)) - - +import os.path + +from base import Setting, SettingSet +from forms import ImageFormWidget + +from django.utils.translation import ugettext_lazy as _ +from django.forms.widgets import Textarea + +BASIC_SET = SettingSet('basic', _('Basic settings'), _("The basic settings for your application"), 1) + +APP_LOGO = Setting('APP_LOGO', '/upfiles/logo.png', BASIC_SET, dict( +label = _("Application logo"), +help_text = _("Your site main logo."), +widget=ImageFormWidget)) + +APP_FAVICON = Setting('APP_FAVICON', '/m/default/media/images/favicon.ico', BASIC_SET, dict( +label = _("Favicon"), +help_text = _("Your site favicon."), +widget=ImageFormWidget)) + +APP_TITLE = Setting('APP_TITLE', u'OSQA: Open Source Q&A Forum', BASIC_SET, dict( +label = _("Application title"), +help_text = _("The title of your application that will show in the browsers title bar"))) + +APP_SHORT_NAME = Setting(u'APP_SHORT_NAME', 'OSQA', BASIC_SET, dict( +label = _("Application short name"), +help_text = "The short name for your application that will show up in many places.")) + +APP_KEYWORDS = Setting('APP_KEYWORDS', u'OSQA,CNPROG,forum,community', BASIC_SET, dict( +label = _("Application keywords"), +help_text = _("The meta keywords that will be available through the HTML meta tags."))) + +APP_DESCRIPTION = Setting('APP_DESCRIPTION', u'Ask and answer questions.', BASIC_SET, dict( +label = _("Application description"), +help_text = _("The description of your application"), +widget=Textarea)) + +APP_COPYRIGHT = Setting('APP_COPYRIGHT', u'Copyright OSQA, 2010. Some rights reserved under creative commons license.', BASIC_SET, dict( +label = _("Copyright notice"), +help_text = _("The copyright notice visible at the footer of your page."))) + +SUPPORT_URL = Setting('SUPPORT_URL', '', BASIC_SET, dict( +label = _("Support URL"), +help_text = _("The URL provided for users to get support. It can be http: or mailto: or whatever your preferred support scheme is."), +required=False)) + +CONTACT_URL = Setting('CONTACT_URL', '', BASIC_SET, dict( +label = _("Contact URL"), +help_text = _("The URL provided for users to contact you. It can be http: or mailto: or whatever your preferred contact scheme is."), +required=False)) + + diff --git a/forum/settings/email.py b/forum/settings/email.py index 71a7136..896efbd 100644 --- a/forum/settings/email.py +++ b/forum/settings/email.py @@ -1,86 +1,86 @@ -from base import Setting, SettingSet -from django.utils.translation import ugettext_lazy as _ -from django.forms.widgets import PasswordInput -from django.forms.widgets import RadioSelect -from forms import TestEmailSettingsWidget - -EMAIL_SET = SettingSet('email', _('Email settings'), _("Email server and other email related settings."), 50) - -TEST_EMAIL_SETTINGS = Setting('TEST_EMAIL_SETTINGS', '', EMAIL_SET, dict( -label = _("E-Mail settings test"), -help_text = _("Test the current E-Mail configuration."), -required=False, -widget=TestEmailSettingsWidget)) - -EMAIL_HOST = Setting('EMAIL_HOST', '', EMAIL_SET, dict( -label = _("Email Server"), -help_text = _("The SMTP server through which your application will be sending emails."), -required=False)) - -EMAIL_PORT = Setting('EMAIL_PORT', 25, EMAIL_SET, dict( -label = _("Email Port"), -help_text = _("The port on which your SMTP server is listening to. Usually this is 25, but can be something else."), -required=False)) - -EMAIL_HOST_USER = Setting('EMAIL_HOST_USER', '', EMAIL_SET, dict( -label = _("Email User"), -help_text = _("The username for your SMTP connection."), -required=False)) - -EMAIL_HOST_PASSWORD = Setting('EMAIL_HOST_PASSWORD', '', EMAIL_SET, dict( -label = _("Email Password"), -help_text = _("The password for your SMTP connection."), -required=False, -widget=PasswordInput(render_value=True))) - -EMAIL_USE_TLS = Setting('EMAIL_USE_TLS', False, EMAIL_SET, dict( -label = _("Use TLS"), -help_text = _("Whether to use TLS for authentication with your SMTP server."), -required=False)) - -DEFAULT_FROM_EMAIL = Setting('DEFAULT_FROM_EMAIL', '', EMAIL_SET, dict( -label = _("Site 'from' Email Address"), -help_text = _("The address that will show up on the 'from' field on emails sent by your website."), -required=False)) - -DEFAULT_REPLY_TO_EMAIL = Setting('DEFAULT_REPLY_TO_EMAIL', '', EMAIL_SET, dict( -label = _("Site 'reply-to' Email Address"), -help_text = _("The address that will show up on the 'reply-to' field on emails sent by your website."), -required=False)) - -EMAIL_SUBJECT_PREFIX = Setting('EMAIL_SUBJECT_PREFIX', '', EMAIL_SET, dict( -label = _("Email Subject Prefix"), -help_text = _("Every email sent through your website will have the subject prefixed by this string. It's usually a good idea to have such a prefix so your users can easily set up a filter on their email clients."), -required=False)) - -EMAIL_FOOTER_TEXT = Setting(u'EMAIL_FOOTER_TEXT', '', EMAIL_SET, dict( -label = _("Email Footer Text"), -help_text = _("Email footer text, usually \"CAN SPAM\" compliance, or the physical address of the organization running the website. See this Wikipedia article for more info."), -required=False)) - -EMAIL_BORDER_COLOR = Setting('EMAIL_BORDER_COLOR', '#e5ebf8', EMAIL_SET, dict( -label = _("Email Border Color"), -help_text = _("The outter border color of the email base template"), -required=False)) - -EMAIL_PARAGRAPH_STYLE = Setting('EMAIL_PARAGRAPH_STYLE', "color:#333333;font-family:'helvetica neue', arial, Helvetica, sans-serif;line-height:18px;font-size:14px;margin-top:10px;", EMAIL_SET, dict( -label = _("Email Paragraph Style"), -help_text = _("A valid css string to be used to style email paragraphs (the P tag)."), -required=False)) - -EMAIL_ANCHOR_STYLE = Setting('EMAIL_ANCHOR_STYLE', "text-decoration:none;color:#3060a8;font-weight:bold;", EMAIL_SET, dict( -label = _("Email Link Style"), -help_text = _("A valid css string to be used to style email links (the A tag)."), -required=False)) - -SEND_DIGEST_ONLY_TO_ACTIVE_USERS = Setting('SEND_DIGEST_ONLY_TO_ACTIVE_USERS', True, EMAIL_SET, dict( -label = _("Send digest only to active users"), -help_text = _("If checked the daily digest won't be sent to users that have been suspended."), -required=False)) - -SEND_DIGEST_ONLY_TO_VALIDATED_USERS = Setting('SEND_DIGEST_ONLY_TO_VALIDATED_USERS', True, EMAIL_SET, dict( -label = _("Send digest only to validated users"), -help_text = _("If checked the daily digest won't be sent to users that haven't validated their emails."), -required=False)) - -EMAIL_DIGEST_FLAG = Setting('EMAIL_DIGEST_FLAG', None) +from base import Setting, SettingSet +from django.utils.translation import ugettext_lazy as _ +from django.forms.widgets import PasswordInput +from django.forms.widgets import RadioSelect +from forms import TestEmailSettingsWidget + +EMAIL_SET = SettingSet('email', _('Email settings'), _("Email server and other email related settings."), 50) + +TEST_EMAIL_SETTINGS = Setting('TEST_EMAIL_SETTINGS', '', EMAIL_SET, dict( +label = _("E-Mail settings test"), +help_text = _("Test the current E-Mail configuration."), +required=False, +widget=TestEmailSettingsWidget)) + +EMAIL_HOST = Setting('EMAIL_HOST', '', EMAIL_SET, dict( +label = _("Email Server"), +help_text = _("The SMTP server through which your application will be sending emails."), +required=False)) + +EMAIL_PORT = Setting('EMAIL_PORT', 25, EMAIL_SET, dict( +label = _("Email Port"), +help_text = _("The port on which your SMTP server is listening to. Usually this is 25, but can be something else."), +required=False)) + +EMAIL_HOST_USER = Setting('EMAIL_HOST_USER', '', EMAIL_SET, dict( +label = _("Email User"), +help_text = _("The username for your SMTP connection."), +required=False)) + +EMAIL_HOST_PASSWORD = Setting('EMAIL_HOST_PASSWORD', '', EMAIL_SET, dict( +label = _("Email Password"), +help_text = _("The password for your SMTP connection."), +required=False, +widget=PasswordInput(render_value=True))) + +EMAIL_USE_TLS = Setting('EMAIL_USE_TLS', False, EMAIL_SET, dict( +label = _("Use TLS"), +help_text = _("Whether to use TLS for authentication with your SMTP server."), +required=False)) + +DEFAULT_FROM_EMAIL = Setting('DEFAULT_FROM_EMAIL', '', EMAIL_SET, dict( +label = _("Site 'from' Email Address"), +help_text = _("The address that will show up on the 'from' field on emails sent by your website."), +required=False)) + +DEFAULT_REPLY_TO_EMAIL = Setting('DEFAULT_REPLY_TO_EMAIL', '', EMAIL_SET, dict( +label = _("Site 'reply-to' Email Address"), +help_text = _("The address that will show up on the 'reply-to' field on emails sent by your website."), +required=False)) + +EMAIL_SUBJECT_PREFIX = Setting('EMAIL_SUBJECT_PREFIX', '', EMAIL_SET, dict( +label = _("Email Subject Prefix"), +help_text = _("Every email sent through your website will have the subject prefixed by this string. It's usually a good idea to have such a prefix so your users can easily set up a filter on their email clients."), +required=False)) + +EMAIL_FOOTER_TEXT = Setting(u'EMAIL_FOOTER_TEXT', '', EMAIL_SET, dict( +label = _("Email Footer Text"), +help_text = _("Email footer text, usually \"CAN SPAM\" compliance, or the physical address of the organization running the website. See this Wikipedia article for more info."), +required=False)) + +EMAIL_BORDER_COLOR = Setting('EMAIL_BORDER_COLOR', '#e5ebf8', EMAIL_SET, dict( +label = _("Email Border Color"), +help_text = _("The outter border color of the email base template"), +required=False)) + +EMAIL_PARAGRAPH_STYLE = Setting('EMAIL_PARAGRAPH_STYLE', "color:#333333;font-family:'helvetica neue', arial, Helvetica, sans-serif;line-height:18px;font-size:14px;margin-top:10px;", EMAIL_SET, dict( +label = _("Email Paragraph Style"), +help_text = _("A valid css string to be used to style email paragraphs (the P tag)."), +required=False)) + +EMAIL_ANCHOR_STYLE = Setting('EMAIL_ANCHOR_STYLE', "text-decoration:none;color:#3060a8;font-weight:bold;", EMAIL_SET, dict( +label = _("Email Link Style"), +help_text = _("A valid css string to be used to style email links (the A tag)."), +required=False)) + +SEND_DIGEST_ONLY_TO_ACTIVE_USERS = Setting('SEND_DIGEST_ONLY_TO_ACTIVE_USERS', True, EMAIL_SET, dict( +label = _("Send digest only to active users"), +help_text = _("If checked the daily digest won't be sent to users that have been suspended."), +required=False)) + +SEND_DIGEST_ONLY_TO_VALIDATED_USERS = Setting('SEND_DIGEST_ONLY_TO_VALIDATED_USERS', True, EMAIL_SET, dict( +label = _("Send digest only to validated users"), +help_text = _("If checked the daily digest won't be sent to users that haven't validated their emails."), +required=False)) + +EMAIL_DIGEST_FLAG = Setting('EMAIL_DIGEST_FLAG', None) diff --git a/forum/settings/extkeys.py b/forum/settings/extkeys.py index 9d439df..11b43e3 100644 --- a/forum/settings/extkeys.py +++ b/forum/settings/extkeys.py @@ -1,15 +1,15 @@ -from base import Setting, SettingSet -from django.utils.translation import ugettext_lazy as _ - -EXT_KEYS_SET = SettingSet('extkeys', _('External Keys'), _("Keys for various external providers that your application may optionally use."), 100) - -GOOGLE_SITEMAP_CODE = Setting('GOOGLE_SITEMAP_CODE', '', EXT_KEYS_SET, dict( -label = _("Google sitemap code"), -help_text = _("This is the code you get when you register your site at Google webmaster central."), -required=False)) - -GOOGLE_ANALYTICS_KEY = Setting('GOOGLE_ANALYTICS_KEY', '', EXT_KEYS_SET, dict( -label = _("Google analytics key"), -help_text = _("Your Google analytics key. You can get one at the Google analytics official website"), -required=False)) - +from base import Setting, SettingSet +from django.utils.translation import ugettext_lazy as _ + +EXT_KEYS_SET = SettingSet('extkeys', _('External Keys'), _("Keys for various external providers that your application may optionally use."), 100) + +GOOGLE_SITEMAP_CODE = Setting('GOOGLE_SITEMAP_CODE', '', EXT_KEYS_SET, dict( +label = _("Google sitemap code"), +help_text = _("This is the code you get when you register your site at Google webmaster central."), +required=False)) + +GOOGLE_ANALYTICS_KEY = Setting('GOOGLE_ANALYTICS_KEY', '', EXT_KEYS_SET, dict( +label = _("Google analytics key"), +help_text = _("Your Google analytics key. You can get one at the Google analytics official website"), +required=False)) + diff --git a/forum/settings/minrep.py b/forum/settings/minrep.py index 1c9e17d..a8dac0d 100644 --- a/forum/settings/minrep.py +++ b/forum/settings/minrep.py @@ -1,95 +1,95 @@ -from base import Setting, SettingSet -from django.utils.translation import ugettext_lazy as _ - -MIN_REP_SET = SettingSet('minrep', _('Minimum reputation config'), _("Configure the minimum reputation required to perform certain actions on your site."), 300) - -CAPTCHA_IF_REP_LESS_THAN = Setting('CAPTCHA_IF_REP_LESS_THAN', 0, MIN_REP_SET, dict( -label = _("Show captcha if user with less reputation than"), -help_text = _("If the user has less reputation, captcha is used to when adding new content."))) - -REP_TO_VOTE_UP = Setting('REP_TO_VOTE_UP', 15, MIN_REP_SET, dict( -label = _("Minimum reputation to vote up"), -help_text = _("The minimum reputation an user must have to be allowed to vote up."))) - -REP_TO_VOTE_DOWN = Setting('REP_TO_VOTE_DOWN', 100, MIN_REP_SET, dict( -label = _("Minimum reputation to vote down"), -help_text = _("The minimum reputation an user must have to be allowed to vote down."))) - -REP_TO_FLAG = Setting('REP_TO_FLAG', 15, MIN_REP_SET, dict( -label = _("Minimum reputation to flag a post"), -help_text = _("The minimum reputation an user must have to be allowed to flag a post."))) - -REP_TO_COMMENT = Setting('REP_TO_COMMENT', 50, MIN_REP_SET, dict( -label = _("Minimum reputation to comment"), -help_text = _("The minimum reputation an user must have to be allowed to comment a post."))) - -REP_TO_LIKE_COMMENT = Setting('REP_TO_LIKE_COMMENT', 15, MIN_REP_SET, dict( -label = _("Minimum reputation to like a comment"), -help_text = _("The minimum reputation an user must have to be allowed to \"like\" a comment."))) - -REP_TO_UPLOAD = Setting('REP_TO_UPLOAD', 60, MIN_REP_SET, dict( -label = _("Minimum reputation to upload"), -help_text = _("The minimum reputation an user must have to be allowed to upload a file."))) - -REP_TO_CREATE_TAGS = Setting('REP_TO_CREATE_TAGS', 250, MIN_REP_SET, dict( -label = _("Minimum reputation to create tags"), -help_text = _("The minimum reputation an user must have to be allowed to create new tags."))) - -REP_TO_CLOSE_OWN = Setting('REP_TO_CLOSE_OWN', 250, MIN_REP_SET, dict( -label = _("Minimum reputation to close own question"), -help_text = _("The minimum reputation an user must have to be allowed to close his own question."))) - -UNIFY_PERMISSIONS_TO_CLOSE_AND_REOPEN = Setting('UNIFY_PERMISSIONS_TO_CLOSE_AND_REOPEN', True, MIN_REP_SET, dict( -label = _("Unify close and reopen permissions"), -help_text = _("If checked the same permissions as the ones to close question will be required to reopen it."), -required=False)) - -REP_TO_REOPEN_OWN = Setting('REP_TO_REOPEN_OWN', 500, MIN_REP_SET, dict( -label = _("Minimum reputation to reopen own question"), -help_text = _("The minimum reputation an user must have to be allowed to reopen his own question."))) - -REP_TO_RETAG = Setting('REP_TO_RETAG', 500, MIN_REP_SET, dict( -label = _("Minimum reputation to retag others questions"), -help_text = _("The minimum reputation an user must have to be allowed to retag others questions."))) - -REP_TO_EDIT_WIKI = Setting('REP_TO_EDIT_WIKI', 750, MIN_REP_SET, dict( -label = _("Minimum reputation to edit wiki posts"), -help_text = _("The minimum reputation an user must have to be allowed to edit community wiki posts."))) - -REP_TO_WIKIFY = Setting('REP_TO_WIKIFY', 2000, MIN_REP_SET, dict( -label = _("Minimum reputation to mark post as community wiki"), -help_text = _("The minimum reputation an user must have to be allowed to mark a post as community wiki."))) - -REP_TO_EDIT_OTHERS = Setting('REP_TO_EDIT_OTHERS', 2000, MIN_REP_SET, dict( -label = _("Minimum reputation to edit others posts"), -help_text = _("The minimum reputation an user must have to be allowed to edit others posts."))) - -REP_TO_CLOSE_OTHERS = Setting('REP_TO_CLOSE_OTHERS', 3000, MIN_REP_SET, dict( -label = _("Minimum reputation to close others posts"), -help_text = _("The minimum reputation an user must have to be allowed to close others posts."))) - -REP_TO_DELETE_COMMENTS = Setting('REP_TO_DELETE_COMMENTS', 2000, MIN_REP_SET, dict( -label = _("Minimum reputation to delete comments"), -help_text = _("The minimum reputation an user must have to be allowed to delete comments."))) - -REP_TO_CONVERT_TO_COMMENT = Setting('REP_TO_CONVERT_TO_COMMENT', 2000, MIN_REP_SET, dict( -label = _("Minimum reputation to convert answers to comment"), -help_text = _("The minimum reputation an user must have to be allowed to convert an answer into a comment."))) - -REP_TO_CONVERT_COMMENTS_TO_ANSWERS = Setting('REP_TO_CONVERT_COMMENTS_TO_ANSWERS', 2000, MIN_REP_SET, dict( -label = _("Minimum reputation to convert comments to answers"), -help_text = _("The minimum reputation an user must have to be allowed to convert comments into an answer."))) - -REP_TO_CONVERT_TO_QUESTION = Setting('REP_TO_CONVERT_TO_QUESTION', 2000, MIN_REP_SET, dict( -label = _("Minimum reputation to convert answers to questions"), -help_text = _("The minimum reputation an user must have to be allowed to convert an answer into a question."))) - -REP_TO_VIEW_FLAGS = Setting('REP_TO_VIEW_FLAGS', 2000, MIN_REP_SET, dict( -label = _("Minimum reputation to view offensive flags"), -help_text = _("The minimum reputation an user must have to view offensive flags."))) - -#REP_TO_DISABLE_NOFOLLOW = Setting('REP_TO_DISABLE_NOFOLLOW', 2000, MIN_REP_SET, dict( -#label = _("Minimum reputation to disable nofollow"), -#help_text = _(""" -#The minimum reputation an user must have to be allowed to disable the nofollow attribute of a post link. -#"""))) +from base import Setting, SettingSet +from django.utils.translation import ugettext_lazy as _ + +MIN_REP_SET = SettingSet('minrep', _('Minimum reputation config'), _("Configure the minimum reputation required to perform certain actions on your site."), 300) + +CAPTCHA_IF_REP_LESS_THAN = Setting('CAPTCHA_IF_REP_LESS_THAN', 0, MIN_REP_SET, dict( +label = _("Show captcha if user with less reputation than"), +help_text = _("If the user has less reputation, captcha is used to when adding new content."))) + +REP_TO_VOTE_UP = Setting('REP_TO_VOTE_UP', 15, MIN_REP_SET, dict( +label = _("Minimum reputation to vote up"), +help_text = _("The minimum reputation an user must have to be allowed to vote up."))) + +REP_TO_VOTE_DOWN = Setting('REP_TO_VOTE_DOWN', 100, MIN_REP_SET, dict( +label = _("Minimum reputation to vote down"), +help_text = _("The minimum reputation an user must have to be allowed to vote down."))) + +REP_TO_FLAG = Setting('REP_TO_FLAG', 15, MIN_REP_SET, dict( +label = _("Minimum reputation to flag a post"), +help_text = _("The minimum reputation an user must have to be allowed to flag a post."))) + +REP_TO_COMMENT = Setting('REP_TO_COMMENT', 50, MIN_REP_SET, dict( +label = _("Minimum reputation to comment"), +help_text = _("The minimum reputation an user must have to be allowed to comment a post."))) + +REP_TO_LIKE_COMMENT = Setting('REP_TO_LIKE_COMMENT', 15, MIN_REP_SET, dict( +label = _("Minimum reputation to like a comment"), +help_text = _("The minimum reputation an user must have to be allowed to \"like\" a comment."))) + +REP_TO_UPLOAD = Setting('REP_TO_UPLOAD', 60, MIN_REP_SET, dict( +label = _("Minimum reputation to upload"), +help_text = _("The minimum reputation an user must have to be allowed to upload a file."))) + +REP_TO_CREATE_TAGS = Setting('REP_TO_CREATE_TAGS', 250, MIN_REP_SET, dict( +label = _("Minimum reputation to create tags"), +help_text = _("The minimum reputation an user must have to be allowed to create new tags."))) + +REP_TO_CLOSE_OWN = Setting('REP_TO_CLOSE_OWN', 250, MIN_REP_SET, dict( +label = _("Minimum reputation to close own question"), +help_text = _("The minimum reputation an user must have to be allowed to close his own question."))) + +UNIFY_PERMISSIONS_TO_CLOSE_AND_REOPEN = Setting('UNIFY_PERMISSIONS_TO_CLOSE_AND_REOPEN', True, MIN_REP_SET, dict( +label = _("Unify close and reopen permissions"), +help_text = _("If checked the same permissions as the ones to close question will be required to reopen it."), +required=False)) + +REP_TO_REOPEN_OWN = Setting('REP_TO_REOPEN_OWN', 500, MIN_REP_SET, dict( +label = _("Minimum reputation to reopen own question"), +help_text = _("The minimum reputation an user must have to be allowed to reopen his own question."))) + +REP_TO_RETAG = Setting('REP_TO_RETAG', 500, MIN_REP_SET, dict( +label = _("Minimum reputation to retag others questions"), +help_text = _("The minimum reputation an user must have to be allowed to retag others questions."))) + +REP_TO_EDIT_WIKI = Setting('REP_TO_EDIT_WIKI', 750, MIN_REP_SET, dict( +label = _("Minimum reputation to edit wiki posts"), +help_text = _("The minimum reputation an user must have to be allowed to edit community wiki posts."))) + +REP_TO_WIKIFY = Setting('REP_TO_WIKIFY', 2000, MIN_REP_SET, dict( +label = _("Minimum reputation to mark post as community wiki"), +help_text = _("The minimum reputation an user must have to be allowed to mark a post as community wiki."))) + +REP_TO_EDIT_OTHERS = Setting('REP_TO_EDIT_OTHERS', 2000, MIN_REP_SET, dict( +label = _("Minimum reputation to edit others posts"), +help_text = _("The minimum reputation an user must have to be allowed to edit others posts."))) + +REP_TO_CLOSE_OTHERS = Setting('REP_TO_CLOSE_OTHERS', 3000, MIN_REP_SET, dict( +label = _("Minimum reputation to close others posts"), +help_text = _("The minimum reputation an user must have to be allowed to close others posts."))) + +REP_TO_DELETE_COMMENTS = Setting('REP_TO_DELETE_COMMENTS', 2000, MIN_REP_SET, dict( +label = _("Minimum reputation to delete comments"), +help_text = _("The minimum reputation an user must have to be allowed to delete comments."))) + +REP_TO_CONVERT_TO_COMMENT = Setting('REP_TO_CONVERT_TO_COMMENT', 2000, MIN_REP_SET, dict( +label = _("Minimum reputation to convert answers to comment"), +help_text = _("The minimum reputation an user must have to be allowed to convert an answer into a comment."))) + +REP_TO_CONVERT_COMMENTS_TO_ANSWERS = Setting('REP_TO_CONVERT_COMMENTS_TO_ANSWERS', 2000, MIN_REP_SET, dict( +label = _("Minimum reputation to convert comments to answers"), +help_text = _("The minimum reputation an user must have to be allowed to convert comments into an answer."))) + +REP_TO_CONVERT_TO_QUESTION = Setting('REP_TO_CONVERT_TO_QUESTION', 2000, MIN_REP_SET, dict( +label = _("Minimum reputation to convert answers to questions"), +help_text = _("The minimum reputation an user must have to be allowed to convert an answer into a question."))) + +REP_TO_VIEW_FLAGS = Setting('REP_TO_VIEW_FLAGS', 2000, MIN_REP_SET, dict( +label = _("Minimum reputation to view offensive flags"), +help_text = _("The minimum reputation an user must have to view offensive flags."))) + +#REP_TO_DISABLE_NOFOLLOW = Setting('REP_TO_DISABLE_NOFOLLOW', 2000, MIN_REP_SET, dict( +#label = _("Minimum reputation to disable nofollow"), +#help_text = _(""" +#The minimum reputation an user must have to be allowed to disable the nofollow attribute of a post link. +#"""))) diff --git a/forum/settings/repgain.py b/forum/settings/repgain.py index 6b283ec..8326757 100644 --- a/forum/settings/repgain.py +++ b/forum/settings/repgain.py @@ -1,49 +1,49 @@ -from base import Setting, SettingSet -from django.utils.translation import ugettext_lazy as _ - -REP_GAIN_SET = SettingSet('repgain', _('Reputation gains and losses config'), _("Configure the reputation points a user may gain or lose upon certain actions."), 200) - -INITIAL_REP = Setting('INITIAL_REP', 1, REP_GAIN_SET, dict( -label = _("Initial reputation"), -help_text = _("The initial reputation an user gets when he first signs in."))) - -MAX_REP_BY_UPVOTE_DAY = Setting('MAX_REP_BY_UPVOTE_DAY', 200, REP_GAIN_SET, dict( -label = "Max rep by up votes / day", -help_text = _("Maximum reputation a user can gain in one day for being upvoted."))) - -REP_GAIN_BY_EMAIL_VALIDATION = Setting('REP_GAIN_BY_EMAIL_VALIDATION', 10, REP_GAIN_SET, dict( -label = _("Rep gain by e-mail validation"), -help_text = _("Reputation a user gains for validating his e-mail."))) - -REP_GAIN_BY_UPVOTED = Setting('REP_GAIN_BY_UPVOTED', 10, REP_GAIN_SET, dict( -label = _("Rep gain by upvoted"), -help_text = _("Reputation a user gains for having one of his posts up voted."))) - -REP_LOST_BY_DOWNVOTED = Setting('REP_LOST_BY_DOWNVOTED', 2, REP_GAIN_SET, dict( -label = _("Rep lost by downvoted"), -help_text = _("Reputation a user loses for having one of his posts down voted."))) - -REP_LOST_BY_DOWNVOTING = Setting('REP_LOST_BY_DOWNVOTING', 1, REP_GAIN_SET, dict( -label = _("Rep lost by downvoting"), -help_text = _("Reputation a user loses for down voting a post."))) - - -REP_GAIN_BY_ACCEPTED = Setting('REP_GAIN_BY_ACCEPTED', 15, REP_GAIN_SET, dict( -label = _("Rep gain by accepted answer"), -help_text = _("Reputation a user gains for having one of his answers accepted."))) - -REP_GAIN_BY_ACCEPTING = Setting('REP_GAIN_BY_ACCEPTING', 2, REP_GAIN_SET, dict( -label = _("Rep gain by accepting answer"), -help_text = _("Reputation a user gains for accepting an answer to one of his questions."))) - -REP_LOST_BY_FLAGGED = Setting('REP_LOST_BY_FLAGGED', 2, REP_GAIN_SET, dict( -label = _("Rep lost by post flagged"), -help_text = _("Reputation a user loses by having one of his posts flagged."))) - -REP_LOST_BY_FLAGGED_3_TIMES = Setting('REP_LOST_BY_FLAGGED_3_TIMES', 30, REP_GAIN_SET, dict( -label = _("Rep lost by post flagged and hidden"), -help_text = _("Reputation a user loses by having the last revision of one of his posts flagged the enough number of times to hide the post."))) - -REP_LOST_BY_FLAGGED_5_TIMES = Setting('REP_LOST_BY_FLAGGED_5_TIMES', 100, REP_GAIN_SET, dict( -label = _("Rep lost by post flagged and deleted"), +from base import Setting, SettingSet +from django.utils.translation import ugettext_lazy as _ + +REP_GAIN_SET = SettingSet('repgain', _('Reputation gains and losses config'), _("Configure the reputation points a user may gain or lose upon certain actions."), 200) + +INITIAL_REP = Setting('INITIAL_REP', 1, REP_GAIN_SET, dict( +label = _("Initial reputation"), +help_text = _("The initial reputation an user gets when he first signs in."))) + +MAX_REP_BY_UPVOTE_DAY = Setting('MAX_REP_BY_UPVOTE_DAY', 200, REP_GAIN_SET, dict( +label = "Max rep by up votes / day", +help_text = _("Maximum reputation a user can gain in one day for being upvoted."))) + +REP_GAIN_BY_EMAIL_VALIDATION = Setting('REP_GAIN_BY_EMAIL_VALIDATION', 10, REP_GAIN_SET, dict( +label = _("Rep gain by e-mail validation"), +help_text = _("Reputation a user gains for validating his e-mail."))) + +REP_GAIN_BY_UPVOTED = Setting('REP_GAIN_BY_UPVOTED', 10, REP_GAIN_SET, dict( +label = _("Rep gain by upvoted"), +help_text = _("Reputation a user gains for having one of his posts up voted."))) + +REP_LOST_BY_DOWNVOTED = Setting('REP_LOST_BY_DOWNVOTED', 2, REP_GAIN_SET, dict( +label = _("Rep lost by downvoted"), +help_text = _("Reputation a user loses for having one of his posts down voted."))) + +REP_LOST_BY_DOWNVOTING = Setting('REP_LOST_BY_DOWNVOTING', 1, REP_GAIN_SET, dict( +label = _("Rep lost by downvoting"), +help_text = _("Reputation a user loses for down voting a post."))) + + +REP_GAIN_BY_ACCEPTED = Setting('REP_GAIN_BY_ACCEPTED', 15, REP_GAIN_SET, dict( +label = _("Rep gain by accepted answer"), +help_text = _("Reputation a user gains for having one of his answers accepted."))) + +REP_GAIN_BY_ACCEPTING = Setting('REP_GAIN_BY_ACCEPTING', 2, REP_GAIN_SET, dict( +label = _("Rep gain by accepting answer"), +help_text = _("Reputation a user gains for accepting an answer to one of his questions."))) + +REP_LOST_BY_FLAGGED = Setting('REP_LOST_BY_FLAGGED', 2, REP_GAIN_SET, dict( +label = _("Rep lost by post flagged"), +help_text = _("Reputation a user loses by having one of his posts flagged."))) + +REP_LOST_BY_FLAGGED_3_TIMES = Setting('REP_LOST_BY_FLAGGED_3_TIMES', 30, REP_GAIN_SET, dict( +label = _("Rep lost by post flagged and hidden"), +help_text = _("Reputation a user loses by having the last revision of one of his posts flagged the enough number of times to hide the post."))) + +REP_LOST_BY_FLAGGED_5_TIMES = Setting('REP_LOST_BY_FLAGGED_5_TIMES', 100, REP_GAIN_SET, dict( +label = _("Rep lost by post flagged and deleted"), help_text = _("Reputation a user loses by having the last revision of one of his posts flagged the enough number of times to delete the post."))) \ No newline at end of file diff --git a/forum/settings/upload.py b/forum/settings/upload.py index 6e66e00..3c1fd24 100644 --- a/forum/settings/upload.py +++ b/forum/settings/upload.py @@ -1,17 +1,17 @@ -import os.path -from base import Setting, SettingSet -from django.utils.translation import ugettext_lazy as _ - -UPLOAD_SET = SettingSet('paths', _('File upload settings'), _("File uploads related settings."), 600) - -UPFILES_FOLDER = Setting('UPFILES_FOLDER', os.path.join(os.path.dirname(os.path.dirname(__file__)),'upfiles'), UPLOAD_SET, dict( -label = _("Uploaded files folder"), -help_text = _("The filesystem path where uploaded files will be stored. Please note that this folder must exist."))) - -UPFILES_ALIAS = Setting('UPFILES_ALIAS', '/upfiles/', UPLOAD_SET, dict( -label = _("Uploaded files alias"), -help_text = _("The url alias for uploaded files. Notice that if you change this setting, you'll need to restart your site."))) - -ALLOW_MAX_FILE_SIZE = Setting('ALLOW_MAX_FILE_SIZE', 2.5, UPLOAD_SET, dict( -label = _("Max file size"), +import os.path +from base import Setting, SettingSet +from django.utils.translation import ugettext_lazy as _ + +UPLOAD_SET = SettingSet('paths', _('File upload settings'), _("File uploads related settings."), 600) + +UPFILES_FOLDER = Setting('UPFILES_FOLDER', os.path.join(os.path.dirname(os.path.dirname(__file__)),'upfiles'), UPLOAD_SET, dict( +label = _("Uploaded files folder"), +help_text = _("The filesystem path where uploaded files will be stored. Please note that this folder must exist."))) + +UPFILES_ALIAS = Setting('UPFILES_ALIAS', '/upfiles/', UPLOAD_SET, dict( +label = _("Uploaded files alias"), +help_text = _("The url alias for uploaded files. Notice that if you change this setting, you'll need to restart your site."))) + +ALLOW_MAX_FILE_SIZE = Setting('ALLOW_MAX_FILE_SIZE', 2.5, UPLOAD_SET, dict( +label = _("Max file size"), help_text = _("The maximum allowed file size for uploads in mb."))) \ No newline at end of file diff --git a/forum/settings/voting.py b/forum/settings/voting.py index aea9153..e4e81b6 100644 --- a/forum/settings/voting.py +++ b/forum/settings/voting.py @@ -1,32 +1,32 @@ -from base import Setting, SettingSet -from django.utils.translation import ugettext_lazy as _ - -VOTE_RULES_SET = SettingSet('voting', _('Voting rules'), _("Configure the voting rules on your site."), 400) - -USER_REPUTATION_TO_MAX_VOTES = Setting('USER_REPUTATION_TO_MAX_VOTES', True, VOTE_RULES_SET, dict( -label = _("Add reputation to max votes per day"), required=False, -help_text = _("The user reputation is added to the static MAX_VOTES_PER_DAY option. Users with higher reputation can vote more."))) - -MAX_VOTES_PER_DAY = Setting('MAX_VOTES_PER_DAY', 30, VOTE_RULES_SET, dict( -label = _("Maximum votes per day"), -help_text = _("The maximum number of votes an user can cast per day."))) - -START_WARN_VOTES_LEFT = Setting('START_WARN_VOTES_LEFT', 10, VOTE_RULES_SET, dict( -label = _("Start warning about votes left"), -help_text = _("From how many votes left should an user start to be warned about it."))) - -MAX_FLAGS_PER_DAY = Setting('MAX_FLAGS_PER_DAY', 5, VOTE_RULES_SET, dict( -label = _("Maximum flags per day"), -help_text = _("The maximum number of times an can flag a post per day."))) - -FLAG_COUNT_TO_HIDE_POST = Setting('FLAG_COUNT_TO_HIDE_POST', 3, VOTE_RULES_SET, dict( -label = _("Flag count to hide post"), -help_text = _("How many times a post needs to be flagged to be hidden from the main page."))) - -FLAG_COUNT_TO_DELETE_POST = Setting('FLAG_COUNT_TO_DELETE_POST', 5, VOTE_RULES_SET, dict( -label = _("Flag count to delete post"), -help_text = _("How many times a post needs to be flagged to be deleted."))) - -DENY_UNVOTE_DAYS = Setting('DENY_UNVOTE_DAYS', 1, VOTE_RULES_SET, dict( -label = _("Days to cancel a vote"), +from base import Setting, SettingSet +from django.utils.translation import ugettext_lazy as _ + +VOTE_RULES_SET = SettingSet('voting', _('Voting rules'), _("Configure the voting rules on your site."), 400) + +USER_REPUTATION_TO_MAX_VOTES = Setting('USER_REPUTATION_TO_MAX_VOTES', True, VOTE_RULES_SET, dict( +label = _("Add reputation to max votes per day"), required=False, +help_text = _("The user reputation is added to the static MAX_VOTES_PER_DAY option. Users with higher reputation can vote more."))) + +MAX_VOTES_PER_DAY = Setting('MAX_VOTES_PER_DAY', 30, VOTE_RULES_SET, dict( +label = _("Maximum votes per day"), +help_text = _("The maximum number of votes an user can cast per day."))) + +START_WARN_VOTES_LEFT = Setting('START_WARN_VOTES_LEFT', 10, VOTE_RULES_SET, dict( +label = _("Start warning about votes left"), +help_text = _("From how many votes left should an user start to be warned about it."))) + +MAX_FLAGS_PER_DAY = Setting('MAX_FLAGS_PER_DAY', 5, VOTE_RULES_SET, dict( +label = _("Maximum flags per day"), +help_text = _("The maximum number of times an can flag a post per day."))) + +FLAG_COUNT_TO_HIDE_POST = Setting('FLAG_COUNT_TO_HIDE_POST', 3, VOTE_RULES_SET, dict( +label = _("Flag count to hide post"), +help_text = _("How many times a post needs to be flagged to be hidden from the main page."))) + +FLAG_COUNT_TO_DELETE_POST = Setting('FLAG_COUNT_TO_DELETE_POST', 5, VOTE_RULES_SET, dict( +label = _("Flag count to delete post"), +help_text = _("How many times a post needs to be flagged to be deleted."))) + +DENY_UNVOTE_DAYS = Setting('DENY_UNVOTE_DAYS', 1, VOTE_RULES_SET, dict( +label = _("Days to cancel a vote"), help_text = _("How many days an user can cancel a vote after he originaly casted it."))) \ No newline at end of file diff --git a/forum/skins/default/media/iepngfix/iepngfix.htc b/forum/skins/default/media/iepngfix/iepngfix.htc index 909f599..6423ad4 100755 --- a/forum/skins/default/media/iepngfix/iepngfix.htc +++ b/forum/skins/default/media/iepngfix/iepngfix.htc @@ -1,198 +1,198 @@ - - - + + + diff --git a/forum/skins/default/media/iepngfix/iepngfix_tilebg.js b/forum/skins/default/media/iepngfix/iepngfix_tilebg.js index 6a1ff49..4252253 100755 --- a/forum/skins/default/media/iepngfix/iepngfix_tilebg.js +++ b/forum/skins/default/media/iepngfix/iepngfix_tilebg.js @@ -1,173 +1,173 @@ -// IE5.5+ PNG Alpha Fix v2.0 Alpha: Background Tiling Support -// (c) 2008-2009 Angus Turnbull http://www.twinhelix.com - -// This is licensed under the GNU LGPL, version 2.1 or later. -// For details, see: http://creativecommons.org/licenses/LGPL/2.1/ - -var IEPNGFix = window.IEPNGFix || {}; - -IEPNGFix.tileBG = function(elm, pngSrc, ready) { - // Params: A reference to a DOM element, the PNG src file pathname, and a - // hidden "ready-to-run" passed when called back after image preloading. - - var data = this.data[elm.uniqueID], - elmW = Math.max(elm.clientWidth, elm.scrollWidth), - elmH = Math.max(elm.clientHeight, elm.scrollHeight), - bgX = elm.currentStyle.backgroundPositionX, - bgY = elm.currentStyle.backgroundPositionY, - bgR = elm.currentStyle.backgroundRepeat; - - // Cache of DIVs created per element, and image preloader/data. - if (!data.tiles) { - data.tiles = { - elm: elm, - src: '', - cache: [], - img: new Image(), - old: {} - }; - } - var tiles = data.tiles, - pngW = tiles.img.width, - pngH = tiles.img.height; - - if (pngSrc) { - if (!ready && pngSrc != tiles.src) { - // New image? Preload it with a callback to detect dimensions. - tiles.img.onload = function() { - this.onload = null; - IEPNGFix.tileBG(elm, pngSrc, 1); - }; - return tiles.img.src = pngSrc; - } - } else { - // No image? - if (tiles.src) ready = 1; - pngW = pngH = 0; - } - tiles.src = pngSrc; - - if (!ready && elmW == tiles.old.w && elmH == tiles.old.h && - bgX == tiles.old.x && bgY == tiles.old.y && bgR == tiles.old.r) { - return; - } - - // Convert English and percentage positions to pixels. - var pos = { - top: '0%', - left: '0%', - center: '50%', - bottom: '100%', - right: '100%' - }, - x, - y, - pc; - x = pos[bgX] || bgX; - y = pos[bgY] || bgY; - if (pc = x.match(/(\d+)%/)) { - x = Math.round((elmW - pngW) * (parseInt(pc[1]) / 100)); - } - if (pc = y.match(/(\d+)%/)) { - y = Math.round((elmH - pngH) * (parseInt(pc[1]) / 100)); - } - x = parseInt(x); - y = parseInt(y); - - // Handle backgroundRepeat. - var repeatX = { 'repeat': 1, 'repeat-x': 1 }[bgR], - repeatY = { 'repeat': 1, 'repeat-y': 1 }[bgR]; - if (repeatX) { - x %= pngW; - if (x > 0) x -= pngW; - } - if (repeatY) { - y %= pngH; - if (y > 0) y -= pngH; - } - - // Go! - this.hook.enabled = 0; - if (!({ relative: 1, absolute: 1 }[elm.currentStyle.position])) { - elm.style.position = 'relative'; - } - var count = 0, - xPos, - maxX = repeatX ? elmW : x + 0.1, - yPos, - maxY = repeatY ? elmH : y + 0.1, - d, - s, - isNew; - if (pngW && pngH) { - for (xPos = x; xPos < maxX; xPos += pngW) { - for (yPos = y; yPos < maxY; yPos += pngH) { - isNew = 0; - if (!tiles.cache[count]) { - tiles.cache[count] = document.createElement('div'); - isNew = 1; - } - var clipR = Math.max(0, xPos + pngW > elmW ? elmW - xPos : pngW), - clipB = Math.max(0, yPos + pngH > elmH ? elmH - yPos : pngH); - d = tiles.cache[count]; - s = d.style; - s.behavior = 'none'; - s.left = (xPos - parseInt(elm.currentStyle.paddingLeft)) + 'px'; - s.top = yPos + 'px'; - s.width = clipR + 'px'; - s.height = clipB + 'px'; - s.clip = 'rect(' + - (yPos < 0 ? 0 - yPos : 0) + 'px,' + - clipR + 'px,' + - clipB + 'px,' + - (xPos < 0 ? 0 - xPos : 0) + 'px)'; - s.display = 'block'; - if (isNew) { - s.position = 'absolute'; - s.zIndex = -999; - if (elm.firstChild) { - elm.insertBefore(d, elm.firstChild); - } else { - elm.appendChild(d); - } - } - this.fix(d, pngSrc, 0); - count++; - } - } - } - while (count < tiles.cache.length) { - this.fix(tiles.cache[count], '', 0); - tiles.cache[count++].style.display = 'none'; - } - - this.hook.enabled = 1; - - // Cache so updates are infrequent. - tiles.old = { - w: elmW, - h: elmH, - x: bgX, - y: bgY, - r: bgR - }; -}; - - -IEPNGFix.update = function() { - // Update all PNG backgrounds. - for (var i in IEPNGFix.data) { - var t = IEPNGFix.data[i].tiles; - if (t && t.elm && t.src) { - IEPNGFix.tileBG(t.elm, t.src); - } - } -}; -IEPNGFix.update.timer = 0; - -if (window.attachEvent && !window.opera) { - window.attachEvent('onresize', function() { - clearTimeout(IEPNGFix.update.timer); - IEPNGFix.update.timer = setTimeout(IEPNGFix.update, 100); - }); -} +// IE5.5+ PNG Alpha Fix v2.0 Alpha: Background Tiling Support +// (c) 2008-2009 Angus Turnbull http://www.twinhelix.com + +// This is licensed under the GNU LGPL, version 2.1 or later. +// For details, see: http://creativecommons.org/licenses/LGPL/2.1/ + +var IEPNGFix = window.IEPNGFix || {}; + +IEPNGFix.tileBG = function(elm, pngSrc, ready) { + // Params: A reference to a DOM element, the PNG src file pathname, and a + // hidden "ready-to-run" passed when called back after image preloading. + + var data = this.data[elm.uniqueID], + elmW = Math.max(elm.clientWidth, elm.scrollWidth), + elmH = Math.max(elm.clientHeight, elm.scrollHeight), + bgX = elm.currentStyle.backgroundPositionX, + bgY = elm.currentStyle.backgroundPositionY, + bgR = elm.currentStyle.backgroundRepeat; + + // Cache of DIVs created per element, and image preloader/data. + if (!data.tiles) { + data.tiles = { + elm: elm, + src: '', + cache: [], + img: new Image(), + old: {} + }; + } + var tiles = data.tiles, + pngW = tiles.img.width, + pngH = tiles.img.height; + + if (pngSrc) { + if (!ready && pngSrc != tiles.src) { + // New image? Preload it with a callback to detect dimensions. + tiles.img.onload = function() { + this.onload = null; + IEPNGFix.tileBG(elm, pngSrc, 1); + }; + return tiles.img.src = pngSrc; + } + } else { + // No image? + if (tiles.src) ready = 1; + pngW = pngH = 0; + } + tiles.src = pngSrc; + + if (!ready && elmW == tiles.old.w && elmH == tiles.old.h && + bgX == tiles.old.x && bgY == tiles.old.y && bgR == tiles.old.r) { + return; + } + + // Convert English and percentage positions to pixels. + var pos = { + top: '0%', + left: '0%', + center: '50%', + bottom: '100%', + right: '100%' + }, + x, + y, + pc; + x = pos[bgX] || bgX; + y = pos[bgY] || bgY; + if (pc = x.match(/(\d+)%/)) { + x = Math.round((elmW - pngW) * (parseInt(pc[1]) / 100)); + } + if (pc = y.match(/(\d+)%/)) { + y = Math.round((elmH - pngH) * (parseInt(pc[1]) / 100)); + } + x = parseInt(x); + y = parseInt(y); + + // Handle backgroundRepeat. + var repeatX = { 'repeat': 1, 'repeat-x': 1 }[bgR], + repeatY = { 'repeat': 1, 'repeat-y': 1 }[bgR]; + if (repeatX) { + x %= pngW; + if (x > 0) x -= pngW; + } + if (repeatY) { + y %= pngH; + if (y > 0) y -= pngH; + } + + // Go! + this.hook.enabled = 0; + if (!({ relative: 1, absolute: 1 }[elm.currentStyle.position])) { + elm.style.position = 'relative'; + } + var count = 0, + xPos, + maxX = repeatX ? elmW : x + 0.1, + yPos, + maxY = repeatY ? elmH : y + 0.1, + d, + s, + isNew; + if (pngW && pngH) { + for (xPos = x; xPos < maxX; xPos += pngW) { + for (yPos = y; yPos < maxY; yPos += pngH) { + isNew = 0; + if (!tiles.cache[count]) { + tiles.cache[count] = document.createElement('div'); + isNew = 1; + } + var clipR = Math.max(0, xPos + pngW > elmW ? elmW - xPos : pngW), + clipB = Math.max(0, yPos + pngH > elmH ? elmH - yPos : pngH); + d = tiles.cache[count]; + s = d.style; + s.behavior = 'none'; + s.left = (xPos - parseInt(elm.currentStyle.paddingLeft)) + 'px'; + s.top = yPos + 'px'; + s.width = clipR + 'px'; + s.height = clipB + 'px'; + s.clip = 'rect(' + + (yPos < 0 ? 0 - yPos : 0) + 'px,' + + clipR + 'px,' + + clipB + 'px,' + + (xPos < 0 ? 0 - xPos : 0) + 'px)'; + s.display = 'block'; + if (isNew) { + s.position = 'absolute'; + s.zIndex = -999; + if (elm.firstChild) { + elm.insertBefore(d, elm.firstChild); + } else { + elm.appendChild(d); + } + } + this.fix(d, pngSrc, 0); + count++; + } + } + } + while (count < tiles.cache.length) { + this.fix(tiles.cache[count], '', 0); + tiles.cache[count++].style.display = 'none'; + } + + this.hook.enabled = 1; + + // Cache so updates are infrequent. + tiles.old = { + w: elmW, + h: elmH, + x: bgX, + y: bgY, + r: bgR + }; +}; + + +IEPNGFix.update = function() { + // Update all PNG backgrounds. + for (var i in IEPNGFix.data) { + var t = IEPNGFix.data[i].tiles; + if (t && t.elm && t.src) { + IEPNGFix.tileBG(t.elm, t.src); + } + } +}; +IEPNGFix.update.timer = 0; + +if (window.attachEvent && !window.opera) { + window.attachEvent('onresize', function() { + clearTimeout(IEPNGFix.update.timer); + IEPNGFix.update.timer = setTimeout(IEPNGFix.update, 100); + }); +} diff --git a/forum/skins/default/media/js/jquery.caret.js b/forum/skins/default/media/js/jquery.caret.js index d22d511..7a00ee5 100644 --- a/forum/skins/default/media/js/jquery.caret.js +++ b/forum/skins/default/media/js/jquery.caret.js @@ -1,10 +1,10 @@ -/* - * - * Copyright (c) 2010 C. F., Wong (Cloudgen Examplet Store) - * Licensed under the MIT License: - * http://www.opensource.org/licenses/mit-license.php - * - */ -(function(k,e,i,j){k.fn.caret=function(b,l){var a,c,f=this[0],d=k.browser.msie;if(typeof b==="object"&&typeof b.start==="number"&&typeof b.end==="number"){a=b.start;c=b.end}else if(typeof b==="number"&&typeof l==="number"){a=b;c=l}else if(typeof b==="string")if((a=f.value.indexOf(b))>-1)c=a+b[e];else a=null;else if(Object.prototype.toString.call(b)==="[object RegExp]"){b=b.exec(f.value);if(b!=null){a=b.index;c=a+b[0][e]}}if(typeof a!="undefined"){if(d){d=this[0].createTextRange();d.collapse(true); -d.moveStart("character",a);d.moveEnd("character",c-a);d.select()}else{this[0].selectionStart=a;this[0].selectionEnd=c}this[0].focus();return this}else{if(d){c=document.selection;if(this[0].tagName.toLowerCase()!="textarea"){d=this.val();a=c[i]()[j]();a.moveEnd("character",d[e]);var g=a.text==""?d[e]:d.lastIndexOf(a.text);a=c[i]()[j]();a.moveStart("character",-d[e]);var h=a.text[e]}else{a=c[i]();c=a[j]();c.moveToElementText(this[0]);c.setEndPoint("EndToEnd",a);g=c.text[e]-a.text[e];h=g+a.text[e]}}else{g= +/* + * + * Copyright (c) 2010 C. F., Wong (Cloudgen Examplet Store) + * Licensed under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + */ +(function(k,e,i,j){k.fn.caret=function(b,l){var a,c,f=this[0],d=k.browser.msie;if(typeof b==="object"&&typeof b.start==="number"&&typeof b.end==="number"){a=b.start;c=b.end}else if(typeof b==="number"&&typeof l==="number"){a=b;c=l}else if(typeof b==="string")if((a=f.value.indexOf(b))>-1)c=a+b[e];else a=null;else if(Object.prototype.toString.call(b)==="[object RegExp]"){b=b.exec(f.value);if(b!=null){a=b.index;c=a+b[0][e]}}if(typeof a!="undefined"){if(d){d=this[0].createTextRange();d.collapse(true); +d.moveStart("character",a);d.moveEnd("character",c-a);d.select()}else{this[0].selectionStart=a;this[0].selectionEnd=c}this[0].focus();return this}else{if(d){c=document.selection;if(this[0].tagName.toLowerCase()!="textarea"){d=this.val();a=c[i]()[j]();a.moveEnd("character",d[e]);var g=a.text==""?d[e]:d.lastIndexOf(a.text);a=c[i]()[j]();a.moveStart("character",-d[e]);var h=a.text[e]}else{a=c[i]();c=a[j]();c.moveToElementText(this[0]);c.setEndPoint("EndToEnd",a);g=c.text[e]-a.text[e];h=g+a.text[e]}}else{g= f.selectionStart;h=f.selectionEnd}a=f.value.substring(g,h);return{start:g,end:h,text:a,replace:function(m){return f.value.substring(0,g)+m+f.value.substring(h,f.value[e])}}}}})(jQuery,"length","createRange","duplicate"); \ No newline at end of file diff --git a/forum/skins/default/media/js/osqa.admin.js b/forum/skins/default/media/js/osqa.admin.js index ccd7103..cdeeddc 100644 --- a/forum/skins/default/media/js/osqa.admin.js +++ b/forum/skins/default/media/js/osqa.admin.js @@ -1,83 +1,83 @@ -$(function() { - $('.string_list_widget_button').live('click', function() { - $but = $(this); - - if ($but.is('.add')) { - $new = $("
" + - "" + - "" + - "
"); - - $but.before($new); - $new.slideDown('fast'); - } else { - $but.parent().slideUp('fast', function() { - $but.parent().remove(); - }); - } - - return false; - }) - - $('.fieldtool').each(function() { - var $link = $(this); - var $input = $link.parent().parent().find('input, textarea'); - var name = $input.attr('name') - - if ($link.is('.context')) { - $link.click(function() { - var $contextbox = $(''); - $link.replaceWith($contextbox); - }); - } else if ($link.is('.default')) { - if ($input.length == 1 && ($input.is('[type=text]') || $input.is('textarea'))) { - $link.click(function() { - $.post(name + '/', function(data) { - $input.val(data); - }); - }); - } else { - $link.attr('href', name + '/'); - } - } - }); - - $('.url_field').each(function() { - var $input = $(this); - var $anchor = $input.parent().find('.url_field_anchor'); - var app_url = $anchor.attr('href'); - - function rewrite_anchor() { - var val = app_url + '/' + $input.val(); - - $anchor.attr('href', val); - $anchor.html(val); - - } - - $input.keyup(rewrite_anchor); - rewrite_anchor(); - }); - - $('#test_email_settings a.test_button').click(function() { - $('div.test_status').hide('slow') - $('div.ajax_indicator').show('fast') - $.post($(this).attr('href'), function(data) { - $('div.ajax_indicator').hide('fast') - $('div.test_status').html(data) - $('div.test_status').show('slow') - }) - }) -}); - -/* - * Autocomplete - jQuery plugin 1.0.3 - * - * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, J�rn Zaefferer - * - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - */ -(function(a){a.fn.extend({autocomplete:function(b,c){var d=typeof b=="string";c=a.extend({},a.Autocompleter.defaults,{url:d?b:null,data:d?null:b,delay:d?a.Autocompleter.defaults.delay:10,max:c&&!c.scroll?10:150},c);c.highlight=c.highlight||function(e){return e};c.formatMatch=c.formatMatch||c.formatItem;return this.each(function(){new a.Autocompleter(this,c)})},result:function(b){return this.bind("result",b)},search:function(b){return this.trigger("search",[b])},flushCache:function(){return this.trigger("flushCache")},setOptions:function(b){return this.trigger("setOptions",[b])},unautocomplete:function(){return this.trigger("unautocomplete")}});a.Autocompleter=function(l,g){var c={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var b=a(l).attr("autocomplete","off").addClass(g.inputClass);var j;var p="";var m=a.Autocompleter.Cache(g);var e=0;var u;var x={mouseDownOnSelect:false};var r=a.Autocompleter.Select(g,l,d,x);var w;a.browser.opera&&a(l.form).bind("submit.autocomplete",function(){if(w){w=false;return false}});b.bind((a.browser.opera?"keypress":"keydown")+".autocomplete",function(y){u=y.keyCode;switch(y.keyCode){case c.UP:y.preventDefault();if(r.visible()){r.prev()}else{t(0,true)}break;case c.DOWN:y.preventDefault();if(r.visible()){r.next()}else{t(0,true)}break;case c.PAGEUP:y.preventDefault();if(r.visible()){r.pageUp()}else{t(0,true)}break;case c.PAGEDOWN:y.preventDefault();if(r.visible()){r.pageDown()}else{t(0,true)}break;case g.multiple&&a.trim(g.multipleSeparator)==","&&c.COMMA:case c.TAB:case c.RETURN:if(d()){y.preventDefault();w=true;return false}break;case c.ESC:r.hide();break;default:clearTimeout(j);j=setTimeout(t,g.delay);break}}).focus(function(){e++}).blur(function(){e=0;if(!x.mouseDownOnSelect){s()}}).click(function(){if(e++>1&&!r.visible()){t(0,true)}}).bind("search",function(){var y=(arguments.length>1)?arguments[1]:null;function z(D,C){var A;if(C&&C.length){for(var B=0;B1){y=A.slice(0,A.length-1).join(g.multipleSeparator)+g.multipleSeparator+y}y+=g.multipleSeparator}b.val(y);v();b.trigger("result",[z.data,z.value]);return true}function t(A,z){if(u==c.DEL){r.hide();return}var y=b.val();if(!z&&y==p){return}p=y;y=i(y);if(y.length>=g.minChars){b.addClass(g.loadingClass);if(!g.matchCase){y=y.toLowerCase()}f(y,k,v)}else{n();r.hide()}}function h(z){if(!z){return[""]}var A=z.split(g.multipleSeparator);var y=[];a.each(A,function(B,C){if(a.trim(C)){y[B]=a.trim(C)}});return y}function i(y){if(!g.multiple){return y}var z=h(y);return z[z.length-1]}function q(y,z){if(g.autoFill&&(i(b.val()).toLowerCase()==y.toLowerCase())&&u!=c.BACKSPACE){b.val(b.val()+z.substring(i(p).length));a.Autocompleter.Selection(l,p.length,p.length+z.length)}}function s(){clearTimeout(j);j=setTimeout(v,200)}function v(){var y=r.visible();r.hide();clearTimeout(j);n();if(g.mustMatch){b.search(function(z){if(!z){if(g.multiple){var A=h(b.val()).slice(0,-1);b.val(A.join(g.multipleSeparator)+(A.length?g.multipleSeparator:""))}else{b.val("")}}})}if(y){a.Autocompleter.Selection(l,l.value.length,l.value.length)}}function k(z,y){if(y&&y.length&&e){n();r.display(y,z);q(z,y[0].value);r.show()}else{v()}}function f(z,B,y){if(!g.matchCase){z=z.toLowerCase()}var A=m.load(z);if(A&&A.length){B(z,A)}else{if((typeof g.url=="string")&&(g.url.length>0)){var C={timestamp:+new Date()};a.each(g.extraParams,function(D,E){C[D]=typeof E=="function"?E():E});a.ajax({mode:"abort",port:"autocomplete"+l.name,dataType:g.dataType,url:g.url,data:a.extend({q:i(z),limit:g.max},C),success:function(E){var D=g.parse&&g.parse(E)||o(E);m.add(z,D);B(z,D)}})}else{r.emptyList();y(z)}}}function o(B){var y=[];var A=B.split("\n");for(var z=0;z]*)("+b.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"$1")},scroll:true,scrollHeight:180};a.Autocompleter.Cache=function(c){var f={};var d=0;function h(l,k){if(!c.matchCase){l=l.toLowerCase()}var j=l.indexOf(k);if(j==-1){return false}return j==0||c.matchContains}function g(j,i){if(d>c.cacheLength){b()}if(!f[j]){d++}f[j]=i}function e(){if(!c.data){return false}var k={},j=0;if(!c.url){c.cacheLength=1}k[""]=[];for(var m=0,l=c.data.length;m0){var o=f[j];a.each(o,function(p,k){if(h(k.value,n)){m.push(k)}})}}return m}else{if(f[n]){return f[n]}else{if(c.matchSubset){for(var l=n.length-1;l>=c.minChars;l--){var o=f[n.substr(0,l)];if(o){var m=[];a.each(o,function(p,k){if(h(k.value,n)){m[m.length]=k}});return m}}}}}return null}}};a.Autocompleter.Select=function(e,j,l,p){var i={ACTIVE:"ac_over"};var k,f=-1,r,m="",s=true,c,o;function n(){if(!s){return}c=a("
").hide().addClass(e.resultsClass).css("position","absolute").appendTo(document.body);o=a("
    ").appendTo(c).mouseover(function(t){if(q(t).nodeName&&q(t).nodeName.toUpperCase()=="LI"){f=a("li",o).removeClass(i.ACTIVE).index(q(t));a(q(t)).addClass(i.ACTIVE)}}).click(function(t){a(q(t)).addClass(i.ACTIVE);l();j.focus();return false}).mousedown(function(){p.mouseDownOnSelect=true}).mouseup(function(){p.mouseDownOnSelect=false});if(e.width>0){c.css("width",e.width)}s=false}function q(u){var t=u.target;while(t&&t.tagName!="LI"){t=t.parentNode}if(!t){return[]}return t}function h(t){k.slice(f,f+1).removeClass(i.ACTIVE);g(t);var v=k.slice(f,f+1).addClass(i.ACTIVE);if(e.scroll){var u=0;k.slice(0,f).each(function(){u+=this.offsetHeight});if((u+v[0].offsetHeight-o.scrollTop())>o[0].clientHeight){o.scrollTop(u+v[0].offsetHeight-o.innerHeight())}else{if(u=k.size()){f=0}}}function b(t){return e.max&&e.max").html(e.highlight(w,m)).addClass(v%2==0?"ac_even":"ac_odd").appendTo(o)[0];a.data(t,"ac_data",r[v])}k=o.find("li");if(e.selectFirst){k.slice(0,1).addClass(i.ACTIVE);f=0}if(a.fn.bgiframe){o.bgiframe()}}return{display:function(u,t){n();r=u;m=t;d()},next:function(){h(1)},prev:function(){h(-1)},pageUp:function(){if(f!=0&&f-8<0){h(-f)}else{h(-8)}},pageDown:function(){if(f!=k.size()-1&&f+8>k.size()){h(k.size()-1-f)}else{h(8)}},hide:function(){c&&c.hide();k&&k.removeClass(i.ACTIVE);f=-1},visible:function(){return c&&c.is(":visible")},current:function(){return this.visible()&&(k.filter("."+i.ACTIVE)[0]||e.selectFirst&&k[0])},show:function(){var v=a(j).offset();c.css({width:typeof e.width=="string"||e.width>0?e.width:a(j).width(),top:v.top+j.offsetHeight,left:v.left}).show();if(e.scroll){o.scrollTop(0);o.css({maxHeight:e.scrollHeight,overflow:"auto"});if(a.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var t=0;k.each(function(){t+=this.offsetHeight});var u=t>e.scrollHeight;o.css("height",u?e.scrollHeight:t);if(!u){k.width(o.width()-parseInt(k.css("padding-left"))-parseInt(k.css("padding-right")))}}}},selected:function(){var t=k&&k.filter("."+i.ACTIVE).removeClass(i.ACTIVE);return t&&t.length&&a.data(t[0],"ac_data")},emptyList:function(){o&&o.empty()},unbind:function(){c&&c.remove()}}};a.Autocompleter.Selection=function(d,e,c){if(d.setSelectionRange){d.setSelectionRange(e,c)}else{if(d.createTextRange){var b=d.createTextRange();b.collapse(true);b.moveStart("character",e);b.moveEnd("character",c);b.select()}else{if(d.selectionStart){d.selectionStart=e;d.selectionEnd=c}}}d.focus()}})(jQuery); +$(function() { + $('.string_list_widget_button').live('click', function() { + $but = $(this); + + if ($but.is('.add')) { + $new = $("
    " + + "" + + "" + + "
    "); + + $but.before($new); + $new.slideDown('fast'); + } else { + $but.parent().slideUp('fast', function() { + $but.parent().remove(); + }); + } + + return false; + }) + + $('.fieldtool').each(function() { + var $link = $(this); + var $input = $link.parent().parent().find('input, textarea'); + var name = $input.attr('name') + + if ($link.is('.context')) { + $link.click(function() { + var $contextbox = $(''); + $link.replaceWith($contextbox); + }); + } else if ($link.is('.default')) { + if ($input.length == 1 && ($input.is('[type=text]') || $input.is('textarea'))) { + $link.click(function() { + $.post(name + '/', function(data) { + $input.val(data); + }); + }); + } else { + $link.attr('href', name + '/'); + } + } + }); + + $('.url_field').each(function() { + var $input = $(this); + var $anchor = $input.parent().find('.url_field_anchor'); + var app_url = $anchor.attr('href'); + + function rewrite_anchor() { + var val = app_url + '/' + $input.val(); + + $anchor.attr('href', val); + $anchor.html(val); + + } + + $input.keyup(rewrite_anchor); + rewrite_anchor(); + }); + + $('#test_email_settings a.test_button').click(function() { + $('div.test_status').hide('slow') + $('div.ajax_indicator').show('fast') + $.post($(this).attr('href'), function(data) { + $('div.ajax_indicator').hide('fast') + $('div.test_status').html(data) + $('div.test_status').show('slow') + }) + }) +}); + +/* + * Autocomplete - jQuery plugin 1.0.3 + * + * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, J�rn Zaefferer + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ +(function(a){a.fn.extend({autocomplete:function(b,c){var d=typeof b=="string";c=a.extend({},a.Autocompleter.defaults,{url:d?b:null,data:d?null:b,delay:d?a.Autocompleter.defaults.delay:10,max:c&&!c.scroll?10:150},c);c.highlight=c.highlight||function(e){return e};c.formatMatch=c.formatMatch||c.formatItem;return this.each(function(){new a.Autocompleter(this,c)})},result:function(b){return this.bind("result",b)},search:function(b){return this.trigger("search",[b])},flushCache:function(){return this.trigger("flushCache")},setOptions:function(b){return this.trigger("setOptions",[b])},unautocomplete:function(){return this.trigger("unautocomplete")}});a.Autocompleter=function(l,g){var c={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var b=a(l).attr("autocomplete","off").addClass(g.inputClass);var j;var p="";var m=a.Autocompleter.Cache(g);var e=0;var u;var x={mouseDownOnSelect:false};var r=a.Autocompleter.Select(g,l,d,x);var w;a.browser.opera&&a(l.form).bind("submit.autocomplete",function(){if(w){w=false;return false}});b.bind((a.browser.opera?"keypress":"keydown")+".autocomplete",function(y){u=y.keyCode;switch(y.keyCode){case c.UP:y.preventDefault();if(r.visible()){r.prev()}else{t(0,true)}break;case c.DOWN:y.preventDefault();if(r.visible()){r.next()}else{t(0,true)}break;case c.PAGEUP:y.preventDefault();if(r.visible()){r.pageUp()}else{t(0,true)}break;case c.PAGEDOWN:y.preventDefault();if(r.visible()){r.pageDown()}else{t(0,true)}break;case g.multiple&&a.trim(g.multipleSeparator)==","&&c.COMMA:case c.TAB:case c.RETURN:if(d()){y.preventDefault();w=true;return false}break;case c.ESC:r.hide();break;default:clearTimeout(j);j=setTimeout(t,g.delay);break}}).focus(function(){e++}).blur(function(){e=0;if(!x.mouseDownOnSelect){s()}}).click(function(){if(e++>1&&!r.visible()){t(0,true)}}).bind("search",function(){var y=(arguments.length>1)?arguments[1]:null;function z(D,C){var A;if(C&&C.length){for(var B=0;B1){y=A.slice(0,A.length-1).join(g.multipleSeparator)+g.multipleSeparator+y}y+=g.multipleSeparator}b.val(y);v();b.trigger("result",[z.data,z.value]);return true}function t(A,z){if(u==c.DEL){r.hide();return}var y=b.val();if(!z&&y==p){return}p=y;y=i(y);if(y.length>=g.minChars){b.addClass(g.loadingClass);if(!g.matchCase){y=y.toLowerCase()}f(y,k,v)}else{n();r.hide()}}function h(z){if(!z){return[""]}var A=z.split(g.multipleSeparator);var y=[];a.each(A,function(B,C){if(a.trim(C)){y[B]=a.trim(C)}});return y}function i(y){if(!g.multiple){return y}var z=h(y);return z[z.length-1]}function q(y,z){if(g.autoFill&&(i(b.val()).toLowerCase()==y.toLowerCase())&&u!=c.BACKSPACE){b.val(b.val()+z.substring(i(p).length));a.Autocompleter.Selection(l,p.length,p.length+z.length)}}function s(){clearTimeout(j);j=setTimeout(v,200)}function v(){var y=r.visible();r.hide();clearTimeout(j);n();if(g.mustMatch){b.search(function(z){if(!z){if(g.multiple){var A=h(b.val()).slice(0,-1);b.val(A.join(g.multipleSeparator)+(A.length?g.multipleSeparator:""))}else{b.val("")}}})}if(y){a.Autocompleter.Selection(l,l.value.length,l.value.length)}}function k(z,y){if(y&&y.length&&e){n();r.display(y,z);q(z,y[0].value);r.show()}else{v()}}function f(z,B,y){if(!g.matchCase){z=z.toLowerCase()}var A=m.load(z);if(A&&A.length){B(z,A)}else{if((typeof g.url=="string")&&(g.url.length>0)){var C={timestamp:+new Date()};a.each(g.extraParams,function(D,E){C[D]=typeof E=="function"?E():E});a.ajax({mode:"abort",port:"autocomplete"+l.name,dataType:g.dataType,url:g.url,data:a.extend({q:i(z),limit:g.max},C),success:function(E){var D=g.parse&&g.parse(E)||o(E);m.add(z,D);B(z,D)}})}else{r.emptyList();y(z)}}}function o(B){var y=[];var A=B.split("\n");for(var z=0;z]*)("+b.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"$1")},scroll:true,scrollHeight:180};a.Autocompleter.Cache=function(c){var f={};var d=0;function h(l,k){if(!c.matchCase){l=l.toLowerCase()}var j=l.indexOf(k);if(j==-1){return false}return j==0||c.matchContains}function g(j,i){if(d>c.cacheLength){b()}if(!f[j]){d++}f[j]=i}function e(){if(!c.data){return false}var k={},j=0;if(!c.url){c.cacheLength=1}k[""]=[];for(var m=0,l=c.data.length;m0){var o=f[j];a.each(o,function(p,k){if(h(k.value,n)){m.push(k)}})}}return m}else{if(f[n]){return f[n]}else{if(c.matchSubset){for(var l=n.length-1;l>=c.minChars;l--){var o=f[n.substr(0,l)];if(o){var m=[];a.each(o,function(p,k){if(h(k.value,n)){m[m.length]=k}});return m}}}}}return null}}};a.Autocompleter.Select=function(e,j,l,p){var i={ACTIVE:"ac_over"};var k,f=-1,r,m="",s=true,c,o;function n(){if(!s){return}c=a("
    ").hide().addClass(e.resultsClass).css("position","absolute").appendTo(document.body);o=a("
      ").appendTo(c).mouseover(function(t){if(q(t).nodeName&&q(t).nodeName.toUpperCase()=="LI"){f=a("li",o).removeClass(i.ACTIVE).index(q(t));a(q(t)).addClass(i.ACTIVE)}}).click(function(t){a(q(t)).addClass(i.ACTIVE);l();j.focus();return false}).mousedown(function(){p.mouseDownOnSelect=true}).mouseup(function(){p.mouseDownOnSelect=false});if(e.width>0){c.css("width",e.width)}s=false}function q(u){var t=u.target;while(t&&t.tagName!="LI"){t=t.parentNode}if(!t){return[]}return t}function h(t){k.slice(f,f+1).removeClass(i.ACTIVE);g(t);var v=k.slice(f,f+1).addClass(i.ACTIVE);if(e.scroll){var u=0;k.slice(0,f).each(function(){u+=this.offsetHeight});if((u+v[0].offsetHeight-o.scrollTop())>o[0].clientHeight){o.scrollTop(u+v[0].offsetHeight-o.innerHeight())}else{if(u=k.size()){f=0}}}function b(t){return e.max&&e.max").html(e.highlight(w,m)).addClass(v%2==0?"ac_even":"ac_odd").appendTo(o)[0];a.data(t,"ac_data",r[v])}k=o.find("li");if(e.selectFirst){k.slice(0,1).addClass(i.ACTIVE);f=0}if(a.fn.bgiframe){o.bgiframe()}}return{display:function(u,t){n();r=u;m=t;d()},next:function(){h(1)},prev:function(){h(-1)},pageUp:function(){if(f!=0&&f-8<0){h(-f)}else{h(-8)}},pageDown:function(){if(f!=k.size()-1&&f+8>k.size()){h(k.size()-1-f)}else{h(8)}},hide:function(){c&&c.hide();k&&k.removeClass(i.ACTIVE);f=-1},visible:function(){return c&&c.is(":visible")},current:function(){return this.visible()&&(k.filter("."+i.ACTIVE)[0]||e.selectFirst&&k[0])},show:function(){var v=a(j).offset();c.css({width:typeof e.width=="string"||e.width>0?e.width:a(j).width(),top:v.top+j.offsetHeight,left:v.left}).show();if(e.scroll){o.scrollTop(0);o.css({maxHeight:e.scrollHeight,overflow:"auto"});if(a.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var t=0;k.each(function(){t+=this.offsetHeight});var u=t>e.scrollHeight;o.css("height",u?e.scrollHeight:t);if(!u){k.width(o.width()-parseInt(k.css("padding-left"))-parseInt(k.css("padding-right")))}}}},selected:function(){var t=k&&k.filter("."+i.ACTIVE).removeClass(i.ACTIVE);return t&&t.length&&a.data(t[0],"ac_data")},emptyList:function(){o&&o.empty()},unbind:function(){c&&c.remove()}}};a.Autocompleter.Selection=function(d,e,c){if(d.setSelectionRange){d.setSelectionRange(e,c)}else{if(d.createTextRange){var b=d.createTextRange();b.collapse(true);b.moveStart("character",e);b.moveEnd("character",c);b.select()}else{if(d.selectionStart){d.selectionStart=e;d.selectionEnd=c}}}d.focus()}})(jQuery); diff --git a/forum/skins/default/media/js/osqa.main.js b/forum/skins/default/media/js/osqa.main.js index 55a3f1e..0f11ef7 100644 --- a/forum/skins/default/media/js/osqa.main.js +++ b/forum/skins/default/media/js/osqa.main.js @@ -1,1266 +1,1266 @@ -/** - * We do not want the CSRF protection enabled for the AJAX post requests, it causes only trouble. - * Get the csrftoken cookie and pass it to the X-CSRFToken HTTP request property. - */ - -$('html').ajaxSend(function(event, xhr, settings) { - function getCookie(name) { - var cookieValue = null; - if (document.cookie && document.cookie != '') { - var cookies = document.cookie.split(';'); - for (var i = 0; i < cookies.length; i++) { - var cookie = jQuery.trim(cookies[i]); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) == (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - try { - if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { - // Only send the token to relative URLs i.e. locally. - xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); - } - } catch (e) {} -}); - -var response_commands = { - refresh_page: function() { - window.location.reload(true) - }, - - update_post_score: function(id, inc) { - var $score_board = $('#post-' + id + '-score'); - var current = parseInt($score_board.html()) - if (isNaN(current)){ - current = 0; - } - $score_board.html(current + inc) - }, - - update_user_post_vote: function(id, vote_type) { - var $upvote_button = $('#post-' + id + '-upvote'); - var $downvote_button = $('#post-' + id + '-downvote'); - - $upvote_button.removeClass('on'); - $downvote_button.removeClass('on'); - - if (vote_type == 'up') { - $upvote_button.addClass('on'); - } else if (vote_type == 'down') { - $downvote_button.addClass('on'); - } - }, - - update_favorite_count: function(inc) { - var $favorite_count = $('#favorite-count'); - var count = parseInt($favorite_count.html()); - - if (isNaN(count)) - count = 0; - - count += inc; - - if (count == 0) - count = ''; - - $favorite_count.html(count); - }, - - update_favorite_mark: function(type) { - if (type == 'on') { - $('#favorite-mark').addClass('on'); - } else { - $('#favorite-mark').removeClass('on'); - } - }, - - mark_accepted: function(id) { - var $answer = $('#answer-container-' + id); - $answer.addClass('accepted-answer'); - $answer.find('.accept-answer').addClass('on'); - $answer.find('.accept-answer').attr('title', $answer.find('.accept-answer').attr('bn:on')); - }, - - unmark_accepted: function(id) { - var $answer = $('#answer-container-' + id); - $answer.removeClass('accepted-answer'); - $answer.find('.accept-answer').removeClass('on'); - $answer.find('.accept-answer').attr('title', $answer.find('.accept-answer').attr('bn:off')); - }, - - remove_comment: function(id) { - var $comment = $('#comment-' + id); - $comment.css('background', 'red') - $comment.fadeOut('slow', function() { - $comment.remove(); - }); - }, - - award_points: function(id) { - alert('ok'); - }, - - insert_comment: function(post_id, comment_id, comment, username, profile_url, delete_url, edit_url, convert_url, can_convert, show_latest_comments_first) { - var $container = $('#comments-container-' + post_id); - var skeleton = $('#new-comment-skeleton-' + post_id).html().toString(); - - skeleton = skeleton.replace(new RegExp('%ID%', 'g'), comment_id) - .replace(new RegExp('%COMMENT%', 'g'), comment) - .replace(new RegExp('%USERNAME%', 'g'), username) - .replace(new RegExp('%PROFILE_URL%', 'g'), profile_url) - .replace(new RegExp('%DELETE_URL%', 'g'), delete_url) - .replace(new RegExp('%EDIT_URL%', 'g'), edit_url) - .replace(new RegExp('%CONVERT_URL%', 'g'), convert_url); - if (show_latest_comments_first) { - $container.prepend(skeleton); - } else { - $container.append(skeleton); - } - - // Show the convert comment to answer tool only if the current comment can be converted - if (can_convert == true) { - $('#comment-' + comment_id + '-convert').show(); - } - - $('#comment-' + comment_id).slideDown('slow'); - }, - - update_comment: function(comment_id, comment_text) { - var $comment = $('#comment-' + comment_id); - $comment.find('.comment-text').html(comment_text); - - $comment.slideDown('slow'); - }, - - mark_deleted: function(post_type, post_id) { - if (post_type == 'question') { - var $container = $('#question-table'); - $container.addClass('deleted'); - } else { - var $el = $('#' + post_type + '-container-' + post_id); - $el.addClass('deleted'); - } - }, - - unmark_deleted: function(post_type, post_id) { - if (post_type == 'answer') { - var $answer = $('#answer-container-' + post_id); - $answer.removeClass('deleted'); - } else { - var $container = $('#question-table'); - $container.removeClass('deleted'); - } - }, - - set_subscription_button: function(text) { - $('.subscription_switch').html(text); - }, - - set_subscription_status: function(text) { - $('.subscription-status').html(text); - }, - - copy_url: function(url) { - } -} - -function show_dialog (extern) { - var default_close_function = function($diag) { - $diag.fadeOut('fast', function() { - $diag.remove(); - }); - }; - - var options = { - extra_class: '', - pos: { - x: ($(window).width() / 2) + $(window).scrollLeft(), - y: ($(window).height() / 2) + $(window).scrollTop() - }, - dim: false, - yes_text: messages.ok, - yes_callback: default_close_function, - no_text: messages.cancel, - show_no: false, - close_on_clickoutside: false, - copy: false - } - - $.extend(options, extern); - - var copy_id = ''; - if (options.copy) { - copy_id = ' id="copy_clip_button"' - } - - if (options.event != undefined && options.event.pageX != undefined && options.event.pageY != undefined) { - options.pos = {x: options.event.pageX, y: options.event.pageY}; - } else if (options.event.currentTarget != undefined) { - var el = jQuery("#" + options.event.currentTarget.id); - var position = el.offset(); - options.pos = { - x: position.left, - y: position.top - } - } - - var html = ''; - - var $dialog = $(html); - - $('body').append($dialog); - var message = $('.dialog-content')[0]; - message.style.visibility = "hidden"; - - if (options.dim === false) { - $dialog.css({ - visibility: 'hidden', - display: 'block' - }); - - options.dim = {w: $dialog.width(), h: $dialog.height()}; - - $dialog.css({ - width: 1, - height: 1, - visibility: 'visible' - }); - } - - $dialog.css({ - top: options.pos.y, - left: options.pos.x - }); - - top_position_change = (options.dim.h / 2) - left_position_change = (options.dim.w / 2) - - new_top_position = options.pos.y - top_position_change - new_left_position = options.pos.x - left_position_change - - if (new_left_position < 0) { - left_position_change = 0 - } - if (($(window).scrollTop() - new_top_position) > 0) { - top_position_change = 0 - } - if ((options.event.pageY + options.dim.h) > ($(window).height() + $(window).scrollTop())) { - top_position_change = options.dim.h - } - if ((options.event.pageX + options.dim.w) > ($(window).width() + $(window).scrollLeft())) { - left_position_change = options.dim.w - } - - $dialog.animate({ - top: "-=" + top_position_change, - left: "-=" + left_position_change, - width: options.dim.w, - height: options.dim.h - }, 200, function() { - message.style.visibility = "visible"; - }); - - $dialog.find('.dialog-yes').click(function() { - options.yes_callback($dialog); - }); - - if (options.hasOwnProperty("no_callback")) { - $dialog.find('.dialog-no:first-child').click(function() { - options.no_callback($dialog); - }); - } else { - $dialog.find('.dialog-no:first-child').click(function() { - default_close_function($dialog); - }); - } - - if (options.close_on_clickoutside) { - $dialog.one('clickoutside', function() { - default_close_function($dialog); - }); - } - - return $dialog; -} - -function show_message(evt, msg, callback) { - var $dialog = show_dialog({ - html: msg, - extra_class: 'warning', - event: evt, - yes_callback: function() { - $dialog.fadeOut('fast', function() { - $dialog.remove(); - }); - if (callback) { - callback(); - } - }, - close_on_clickoutside: true - }); -} - -function load_prompt(evt, el, url) { - $.get(url, function(data) { - var doptions = { - html: data, - extra_class: 'prompt', - yes_callback: function() { - var postvars = {}; - $dialog.find('input, textarea, select').each(function() { - postvars[$(this).attr('name')] = $(this).val(); - }); - $.post(url, postvars, function(data) { - $dialog.fadeOut('fast', function() { - $dialog.remove(); - }); - process_ajax_response(data, evt); - }, 'json'); - }, - show_no: true, - copy: false - } - - if (el.hasClass('copy')) { - $.extend(doptions, { yes_text : 'Copy', copy: true}); - } - - if (!el.is('.centered')) { - doptions.event = evt; - } - - var $dialog = show_dialog(doptions); - }); -} - -function process_ajax_response(data, evt, callback) { - if (!data.success && data['error_message'] != undefined) { - show_message(evt, data.error_message, function() {if (callback) callback(true);}); - end_command(false); - } - if (typeof data['commands'] != undefined){ - for (var command in data.commands) { - response_commands[command].apply(null, data.commands[command]) - - - } - - if (data['message'] != undefined) { - show_message(evt, data.message, function() {if (callback) callback(false);}) - } else { - if (callback) callback(false); - } - end_command(true); - } -} - -var running = false; - -function start_command() { - $('body').append($('
      ')); - running = true; -} - -function end_command(success) { - if (success) { - $('#command-loader').addClass('success'); - $('#command-loader').fadeOut("slow", function() { - $('#command-loader').remove(); - running = false; - }); - } else { - $('#command-loader').remove(); - running = false; - } -} - -var comment_box_cursor_position = 0; -function canned_comment(post_id, comment) { - textarea = $('#comment-' + post_id + '-form textarea') - - // Get the text from the beginning to the caret - textarea_start = textarea.val().substr(0, comment_box_cursor_position) - - // Get the text from the caret to the end - textarea_end = textarea.val().substr(comment_box_cursor_position, textarea.val().length) - - textarea.val(textarea_start + comment + textarea_end); -} - -$(function() { - $('textarea.commentBox').bind('keydown keyup mousedown mouseup mousemove', function(evt) { - comment_box_cursor_position = $(this).caret().start; - }); - - $('textarea.commentBox').blur(function() { - //alert(comment_box_cursor_position); - }); - - $('a.ajax-command').live('click', function(evt) { - if (running) return false; - - var el = $(this); - - var ajax_url = el.attr('href') - ajax_url = ajax_url + "?nocache=" + new Date().getTime() - - $('.context-menu-dropdown').slideUp('fast'); - - if (el.is('.withprompt')) { - load_prompt(evt, el, ajax_url); - } else if(el.is('.confirm')) { - var doptions = { - html: messages.confirm, - extra_class: 'confirm', - yes_callback: function() { - start_command(); - $.getJSON(ajax_url, function(data) { - process_ajax_response(data, evt); - $dialog.fadeOut('fast', function() { - $dialog.remove(); - }); - }); - }, - yes_text: messages.yes, - show_no: true, - no_text: messages.no - } - - if (!el.is('.centered')) { - doptions.event = evt; - } - var $dialog = show_dialog(doptions); - } else { - start_command(); - $.ajax({ - url: ajax_url, - type: "POST", - dataType: "json", - contentType: "application/json; charset=utf-8", - success: function(data) { - process_ajax_response(data, evt); - } - }); - } - - return false - }); - - $('.context-menu').each(function() { - var $menu = $(this); - var $trigger = $menu.find('.context-menu-trigger'); - var $dropdown = $menu.find('.context-menu-dropdown'); - - $trigger.click(function() { - $dropdown.slideToggle('fast', function() { - if ($dropdown.is(':visible')) { - $dropdown.one('clickoutside', function() { - if ($dropdown.is(':visible')) - $dropdown.slideUp('fast'); - }); - } - }); - }); - }); - - $('div.comment-form-container').each(function() { - var $container = $(this); - var $comment_tools = $container.parent().find('.comment-tools'); - var $comments_container = $container.parent().find('.comments-container'); - - var $form = $container.find('form'); - - if ($form.length) { - var $textarea = $container.find('textarea'); - var textarea = $textarea.get(0); - var $button = $container.find('.comment-submit'); - var $cancel = $container.find('.comment-cancel'); - var $chars_left_message = $container.find('.comments-chars-left-msg'); - var $chars_togo_message = $container.find('.comments-chars-togo-msg'); - var $chars_counter = $container.find('.comments-char-left-count'); - - var $add_comment_link = $comment_tools.find('.add-comment-link'); - - var chars_limits = $chars_counter.html().split('|'); - - var min_length = parseInt(chars_limits[0]); - var max_length = parseInt(chars_limits[1]); - - var warn_length = max_length - 30; - var current_length = 0; - var comment_in_form = false; - var interval = null; - - var hcheck = !($.browser.msie || $.browser.opera); - - $textarea.css("padding-top", 0).css("padding-bottom", 0).css("resize", "none"); - textarea.style.overflow = 'hidden'; - - - function cleanup_form() { - $textarea.val(''); - $textarea.css('height', 80); - $chars_counter.html(max_length); - $chars_left_message.removeClass('warn'); - comment_in_form = false; - current_length = 0; - - $chars_left_message.hide(); - $chars_togo_message.show(); - - $chars_counter.removeClass('warn'); - $chars_counter.html(min_length); - $button.attr("disabled","disabled"); - - interval = null; - } - - cleanup_form(); - - function process_form_changes() { - var length = $textarea.val().replace(/[ ]{2,}/g," ").length; - - if (current_length == length) - return; - - if (length < warn_length && current_length >= warn_length) { - $chars_counter.removeClass('warn'); - } else if (current_length < warn_length && length >= warn_length){ - $chars_counter.addClass('warn'); - } - - if (length < min_length) { - $chars_left_message.hide(); - $chars_togo_message.show(); - $chars_counter.html(min_length - length); - } else { - length = $textarea.val().length; - $chars_togo_message.hide(); - $chars_left_message.show(); - $chars_counter.html(max_length - length); - } - - if (length > max_length || length < min_length) { - $button.attr("disabled","disabled"); - } else { - $button.removeAttr("disabled"); - } - - var current_height = textarea.style.height; - if (hcheck) - textarea.style.height = "0px"; - - var h = Math.max(80, textarea.scrollHeight); - textarea.style.height = current_height; - $textarea.animate({height: h + 'px'}, 50); - - current_length = length; - } - - function show_comment_form() { - $container.slideDown('slow'); - $add_comment_link.fadeOut('slow'); - $textarea.focus(); - interval = window.setInterval(function() { - process_form_changes(); - }, 200); - } - - function hide_comment_form() { - if (interval != null) { - window.clearInterval(interval); - interval = null; - } - $container.slideUp('slow'); - $add_comment_link.fadeIn('slow'); - } - - $add_comment_link.click(function(){ - cleanup_form(); - show_comment_form(); - return false; - }); - - $('#' + $comments_container.attr('id') + ' .comment-edit').live('click', function() { - var $link = $(this); - var comment_id = /comment-(\d+)-edit/.exec($link.attr('id'))[1]; - var $comment = $('#comment-' + comment_id); - - comment_in_form = comment_id; - - $.get($link.attr('href'), function(data) { - $textarea.val(data); - }); - - $comment.slideUp('slow'); - show_comment_form(); - return false; - }); - - $button.click(function(evt) { - if (running) return false; - - var post_data = { - comment: $textarea.val() - } - - if (comment_in_form) { - post_data['id'] = comment_in_form; - } - - start_command(); - $.post($form.attr('action'), post_data, function(data) { - process_ajax_response(data, evt, function(error) { - if (!error) { - cleanup_form(); - hide_comment_form(); - } - }); - - }, "json"); - - return false; - }); - - // Submit comment with CTRL + Enter - $textarea.keydown(function(e) { - if (e.ctrlKey && e.keyCode == 13 && !$button.attr('disabled')) { - // console.log('submit'); - $(this).parent().find('input.comment-submit').click(); - } - }); - - $cancel.click(function(event) { - if (confirm("You will lose all of your changes in this comment. Do you still wish to proceed?")){ - if (comment_in_form) { - $comment = $('#comment-' + comment_in_form).slideDown('slow'); - } - hide_comment_form(); - cleanup_form(); - } - return false; - }); - } - - $comment_tools.find('.show-all-comments-link').click(function() { - $comments_container.find('.not_top_scorer').slideDown('slow'); - $(this).fadeOut('slow'); - $comment_tools.find('.comments-showing').fadeOut('slow'); - return false; - }); - }); - - if ($('#editor').length) { - var $editor = $('#editor'); - var $previewer = $('#previewer'); - var $container = $('#editor-metrics'); - - var initial_whitespace_rExp = /^[^A-Za-zА-Яа-я0-9]+/gi; - var non_alphanumerics_rExp = rExp = /[^A-Za-zА-Яа-я0-9]+/gi; - var editor_interval = null; - - $editor.focus(function() { - if (editor_interval == null) { - editor_interval = window.setInterval(function() { - recalc_metrics(); - }, 200); - } - }); - - function recalc_metrics() { - var text = $previewer.text(); - - var char_count = text.length; - var fullStr = text + " "; - var left_trimmedStr = fullStr.replace(initial_whitespace_rExp, ""); - var cleanedStr = left_trimmedStr.replace(non_alphanumerics_rExp, " "); - var splitString = cleanedStr.split(" "); - var word_count = splitString.length - 1; - - var metrics = char_count + " " + (char_count == 1 ? messages.character : messages.characters); - metrics += " / " + word_count + " " + (word_count == 1 ? messages.word : messages.words); - $container.html(metrics); - } - } -}); - -//var scriptUrl, interestingTags, ignoredTags, tags, $; -function pickedTags(){ - - var sendAjax = function(tagname, reason, action, callback){ - var url = scriptUrl; - if (action == 'add'){ - url += $.i18n._('mark-tag/'); - if (reason == 'good'){ - url += $.i18n._('interesting/'); - } - else { - url += $.i18n._('ignored/'); - } - } - else { - url += $.i18n._('unmark-tag/'); - } - url = url + tagname + '/'; - - var call_settings = { - type:'POST', - url:url, - data: '' - }; - if (callback !== false){ - call_settings.success = callback; - } - $.ajax(call_settings); - }; - - - var unpickTag = function(from_target ,tagname, reason, send_ajax){ - //send ajax request to delete tag - var deleteTagLocally = function(){ - from_target[tagname].remove(); - delete from_target[tagname]; - $(".tags.interesting").trigger('contentchanged'); - }; - - if (send_ajax){ - sendAjax(tagname,reason,'remove',deleteTagLocally); - } - else { - deleteTagLocally(); - } - }; - - var setupTagDeleteEvents = function(obj,tag_store,tagname,reason,send_ajax){ - obj.unbind('mouseover').bind('mouseover', function(){ - $(this).attr('src', mediaUrl('media/images/close-small-hover.png')); - }); - obj.unbind('mouseout').bind('mouseout', function(){ - $(this).attr('src', mediaUrl('media/images/close-small-dark.png')); - }); - obj.click( function(){ - unpickTag(tag_store,tagname,reason,send_ajax); - }); - }; - - var handlePickedTag = function(obj,reason){ - var tagname = $.trim($(obj).prev().attr('value')); - var to_target = interestingTags; - var from_target = ignoredTags; - var to_tag_container; - if (reason == 'bad'){ - to_target = ignoredTags; - from_target = interestingTags; - to_tag_container = $('div .tags.ignored'); - } - else if (reason != 'good'){ - return; - } - else { - to_tag_container = $('div .tags.interesting'); - } - - if (tagname in from_target){ - unpickTag(from_target,tagname,reason,false); - } - - if (!(tagname in to_target)){ - //send ajax request to pick this tag - - sendAjax(tagname,reason,'add',function(){ - var new_tag = $(''); - new_tag.addClass('deletable-tag'); - var tag_link = $(''); - tag_link.attr('rel','tag'); - tag_link.attr('href', scriptUrl + $.i18n._('tags/') + tagname + '/'); - tag_link.html(tagname); - var del_link = $(''); - del_link.addClass('delete-icon'); - del_link.attr('src', mediaUrl('media/images/close-small-dark.png')); - - setupTagDeleteEvents(del_link, to_target, tagname, reason, true); - - new_tag.append(tag_link); - new_tag.append(del_link); - to_tag_container.append(new_tag); - - to_target[tagname] = new_tag; - - to_tag_container.trigger('contentchanged'); - }); - } - }; - - var collectPickedTags = function(){ - var good_prefix = 'interesting-tag-'; - var bad_prefix = 'ignored-tag-'; - var good_re = RegExp('^' + good_prefix); - var bad_re = RegExp('^' + bad_prefix); - interestingTags = {}; - ignoredTags = {}; - $('.deletable-tag').each( - function(i,item){ - var item_id = $(item).attr('id'); - var tag_name, tag_store; - if (good_re.test(item_id)){ - tag_name = item_id.replace(good_prefix,''); - tag_store = interestingTags; - reason = 'good'; - } - else if (bad_re.test(item_id)){ - tag_name = item_id.replace(bad_prefix,''); - tag_store = ignoredTags; - reason = 'bad'; - } - else { - return; - } - tag_store[tag_name] = $(item); - setupTagDeleteEvents($(item).find('img'),tag_store,tag_name,reason,true); - } - ); - }; - - var setupHideIgnoredQuestionsControl = function(){ - $('#hideIgnoredTagsCb').unbind('click').click(function(){ - $.ajax({ - type: 'POST', - dataType: 'json', - cache: false, - url: scriptUrl + $.i18n._('command/'), - data: {command:'toggle-ignored-questions'} - }); - }); - }; - return { - init: function(){ - collectPickedTags(); - setupHideIgnoredQuestionsControl(); - $("#interestingTagInput, #ignoredTagInput").autocomplete(messages.matching_tags_url, { - minChars: 1, - matchContains: true, - max: 20, - /*multiple: false, - the favorite tags and ignore tags don't let you do multiple tags - multipleSeparator: " "*/ - - formatItem: function(row, i, max, value) { - return row[1] + " (" + row[2] + ")"; - }, - - formatResult: function(row, i, max, value){ - return row[1]; - } - - }); - $("#interestingTagAdd").click(function(){handlePickedTag(this,'good');}); - $("#ignoredTagAdd").click(function(){handlePickedTag(this,'bad');}); - } - }; -} - -Hilite={elementid:"content",exact:true,max_nodes:1000,onload:true,style_name:"hilite",style_name_suffix:true,debug_referrer:""};Hilite.search_engines=[["local","q"],["cnprog\\.","q"],["google\\.","q"],["search\\.yahoo\\.","p"],["search\\.msn\\.","q"],["search\\.live\\.","query"],["search\\.aol\\.","userQuery"],["ask\\.com","q"],["altavista\\.","q"],["feedster\\.","q"],["search\\.lycos\\.","q"],["alltheweb\\.","q"],["technorati\\.com/search/([^\\?/]+)",1],["dogpile\\.com/info\\.dogpl/search/web/([^\\?/]+)",1,true]];Hilite.decodeReferrer=function(d){var g=null;var e=new RegExp("");for(var c=0;c2&&f[2]){a=decodeURIComponent(a)}a=a.replace(/\'|"/g,"");a=a.split(/[\s,\+\.]+/);return a}break}}return null};Hilite.decodeReferrerQS=function(f,d){var b=f.indexOf("?");var c;if(b>=0){var a=new String(f.substring(b+1));b=0;c=0;while((b>=0)&&((c=a.indexOf("=",b))>=0)){var e,g;e=a.substring(b,c);b=a.indexOf("&",c)+1;if(e==d){if(b<=0){return a.substring(c+1)}else{return a.substring(c+1,b-1)}}else{if(b<=0){return null}}}}return null};Hilite.hiliteElement=function(f,e){if(!e||f.childNodes.length==0){return}var c=new Array();for(var b=0;b0){c++;if(c>=Hilite.max_nodes){var b=function(){Hilite.walkElements(d,f,e)};setTimeout(b,50);return}if(d.nodeType==1){if(!a.test(d.tagName)&&d.childNodes.length>0){d=d.childNodes[0];f++;continue}}else{if(d.nodeType==3){d=e(d)}}if(d.nextSibling){d=d.nextSibling}else{while(f>0){d=d.parentNode;f--;if(d.nextSibling){d=d.nextSibling;break}}}}};if(Hilite.onload){if(window.attachEvent){window.attachEvent("onload",Hilite.hilite)}else{if(window.addEventListener){window.addEventListener("load",Hilite.hilite,false)}else{var __onload=window.onload;window.onload=function(){Hilite.hilite();__onload()}}}}; - -var mediaUrl = function(resource){ - return scriptUrl + 'm/' + osqaSkin + '/' + resource; -}; - -/* - * jQuery i18n plugin - * @requires jQuery v1.1 or later - * - * Examples at: http://recurser.com/articles/2008/02/21/jquery-i18n-translation-plugin/ - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - * Based on 'javascript i18n that almost doesn't suck' by markos - * http://markos.gaivo.net/blog/?p=100 - * - * Revision: $Id$ - * Version: 1.0.0 Feb-10-2008 - */ - (function($) { -/** - * i18n provides a mechanism for translating strings using a jscript dictionary. - * - */ - - -/* - * i18n property list - */ -$.i18n = { - -/** - * setDictionary() - * Initialise the dictionary and translate nodes - * - * @param property_list i18n_dict : The dictionary to use for translation - */ - setDictionary: function(i18n_dict) { - i18n_dict = i18n_dict; - }, - -/** - * _() - * The actual translation function. Looks the given string up in the - * dictionary and returns the translation if one exists. If a translation - * is not found, returns the original word - * - * @param string str : The string to translate - * @param property_list params : params for using printf() on the string - * @return string : Translated word - * - */ - _: function (str, params) { - var transl = str; - if (i18n_dict&& i18n_dict[str]) { - transl = i18n_dict[str]; - } - return this.printf(transl, params); - }, - -/** - * toEntity() - * Change non-ASCII characters to entity representation - * - * @param string str : The string to transform - * @return string result : Original string with non-ASCII content converted to entities - * - */ - toEntity: function (str) { - var result = ''; - for (var i=0;i 128) - result += "&#"+str.charCodeAt(i)+";"; - else - result += str.charAt(i); - } - return result; - }, - -/** - * stripStr() - * - * @param string str : The string to strip - * @return string result : Stripped string - * - */ - stripStr: function(str) { - return str.replace(/^\s*/, "").replace(/\s*$/, ""); - }, - -/** - * stripStrML() - * - * @param string str : The multi-line string to strip - * @return string result : Stripped string - * - */ - stripStrML: function(str) { - // Split because m flag doesn't exist before JS1.5 and we need to - // strip newlines anyway - var parts = str.split('\n'); - for (var i=0; i15 points requried to upvote':'??+15?????????', - '>100 points required to downvote':'??+100?????????', - 'please see': '??', - 'cannot vote for own posts':'??????????', - 'daily vote cap exhausted':'????????????????', - 'cannot revoke old vote':'??????????????', - 'please confirm offensive':"??????????????????????", - 'anonymous users cannot flag offensive posts':'???????????', - 'cannot flag message as offensive twice':'???????', - 'flag offensive cap exhausted':'?????????????5?‘??’???', - 'need >15 points to report spam':"??+15??????‘???’?", - 'confirm delete':"?????/????????", - 'anonymous users cannot delete/undelete':"???????????????", - 'post recovered':"?????????????", - 'post deleted':"????????????", - 'add comment':'????', - 'community karma points':'????', - 'to comment, need':'????', - 'delete this comment':'?????', - 'hide comments':"????", - 'add a comment':"????", - 'comments':"??", - 'confirm delete comment':"?????????", - 'characters':'??', - 'can write':'???', - 'click to close':'???????', - 'loading...':'???...', - 'tags cannot be empty':'???????', - 'tablimits info':"??5????????????20????", - 'content cannot be empty':'???????', - 'content minchars': '????? {0} ???', - 'please enter title':'??????', - 'title minchars':"????? {0} ???", - 'delete':'??', - 'undelete': '??', - 'bold':'??', - 'italic':'??', - 'link':'???', - 'quote':'??', - 'preformatted text':'??', - 'image':'??', - 'numbered list':'??????', - 'bulleted list':'??????', - 'heading':'??', - 'horizontal bar':'???', - 'undo':'??', - 'redo':'??', - 'enter image url':'??????

      ???
      http://www.example.com/image.jpg \"????\"', - 'enter url':'??Web??

      ???
      http://www.cnprog.com/ \"????\"

      "', - 'upload image':'?????????' -}; - -var i18nEn = { - 'need >15 points to report spam':'need >15 points to report spam ', - '>15 points requried to upvote':'>15 points required to upvote ', - 'tags cannot be empty':'please enter at least one tag', - 'anonymous users cannot vote':'sorry, anonymous users cannot vote ', - 'anonymous users cannot select favorite questions':'sorry, anonymous users cannot select favorite questions ', - 'to comment, need': '(to comment other people\'s posts, karma ', - 'please see':'please see ', - 'community karma points':' or more is necessary) - ', - 'upload image':'Upload image:', - 'enter image url':'enter URL of the image, e.g. http://www.example.com/image.jpg \"image title\"', - 'enter url':'enter Web address, e.g. http://www.example.com \"page title\"', - 'daily vote cap exhausted':'sorry, you\'ve used up todays vote cap', - 'cannot pick own answer as best':'sorry, you cannot accept your own answer', - 'cannot revoke old vote':'sorry, older votes cannot be revoked', - 'please confirm offensive':'are you sure this post is offensive, contains spam, advertising, malicious remarks, etc.?', - 'flag offensive cap exhausted':'sorry, you\'ve used up todays cap of flagging offensive messages ', - 'confirm delete':'are you sure you want to delete this?', - 'anonymous users cannot delete/undelete':'sorry, anonymous users cannot delete or undelete posts', - 'post recovered':'your post is now restored!', - 'post deleted':'your post has been deleted', - 'confirm delete comment':'do you really want to delete this comment?', - 'can write':'have ', - 'tablimits info':'up to 5 tags, no more than 20 characters each', - 'content minchars': 'please enter more than {0} characters', - 'title minchars':"please enter at least {0} characters", - 'characters':'characters left', - 'cannot vote for own posts':'sorry, you cannot vote for your own posts', - 'cannot flag message as offensive twice':'cannot flag message as offensive twice ', - '>100 points required to downvote':'>100 points required to downvote ' -}; - -var i18nEs = { - 'insufficient privilege':'privilegio insuficiente', - 'cannot pick own answer as best':'no puede escoger su propia respuesta como la mejor', - 'anonymous users cannot select favorite questions':'usuarios anonimos no pueden seleccionar', - 'please login':'por favor inicie sesión', - 'anonymous users cannot vote':'usuarios anónimos no pueden votar', - '>15 points requried to upvote': '>15 puntos requeridos para votar positivamente', - '>100 points required to downvote':'>100 puntos requeridos para votar negativamente', - 'please see': 'por favor vea', - 'cannot vote for own posts':'no se puede votar por sus propias publicaciones', - 'daily vote cap exhausted':'cuota de votos diarios excedida', - 'cannot revoke old vote':'no puede revocar un voto viejo', - 'please confirm offensive':"por favor confirme ofensiva", - 'anonymous users cannot flag offensive posts':'usuarios anónimos no pueden marcar publicaciones como ofensivas', - 'cannot flag message as offensive twice':'no puede marcar mensaje como ofensivo dos veces', - 'flag offensive cap exhausted':'cuota para marcar ofensivas ha sido excedida', - 'need >15 points to report spam':"necesita >15 puntos para reportar spam", - 'confirm delete':"¿Está seguro que desea borrar esto?", - 'anonymous users cannot delete/undelete':"usuarios anónimos no pueden borrar o recuperar publicaciones", - 'post recovered':"publicación recuperada", - 'post deleted':"publicación borrada?", - 'add comment':'agregar comentario', - 'community karma points':'reputación comunitaria', - 'to comment, need':'para comentar, necesita reputación', - 'delete this comment':'borrar este comentario', - 'hide comments':"ocultar comentarios", - 'add a comment':"agregar comentarios", - 'comments':"comentarios", - 'confirm delete comment':"¿Realmente desea borrar este comentario?", - 'characters':'caracteres faltantes', - 'can write':'tiene ', - 'click to close':'haga click para cerrar', - 'loading...':'cargando...', - 'tags cannot be empty':'las etiquetas no pueden estar vacías', - 'tablimits info':"hasta 5 etiquetas de no mas de 20 caracteres cada una", - 'content cannot be empty':'el contenido no puede estar vacío', - 'content minchars': 'por favor introduzca mas de {0} caracteres', - 'please enter title':'por favor ingrese un título', - 'title minchars':"por favor introduzca al menos {0} caracteres", - 'delete':'borrar', - 'undelete': 'recuperar', - 'bold': 'negrita', - 'italic':'cursiva', - 'link':'enlace', - 'quote':'citar', - 'preformatted text':'texto preformateado', - 'image':'imagen', - 'numbered list':'lista numerada', - 'bulleted list':'lista no numerada', - 'heading':'??', - 'horizontal bar':'barra horizontal', - 'undo':'deshacer', - 'redo':'rehacer', - 'enter image url':'introduzca la URL de la imagen, por ejemplo?
      http://www.example.com/image.jpg \"titulo de imagen\"', - 'enter url':'introduzca direcciones web, ejemplo?
      http://www.cnprog.com/ \"titulo del enlace\"

      "', - 'upload image':'cargar imagen?', - 'questions/' : 'preguntas/', - 'vote/' : 'votar/' -}; - -var i18n = { - 'en':i18nEn, - 'zh_CN':i18nZh, - 'es':i18nEs -}; - -var i18n_dict = i18n[i18nLang]; - -/* - jQuery TextAreaResizer plugin - Created on 17th January 2008 by Ryan O'Dell - Version 1.0.4 -*/(function($){var textarea,staticOffset;var iLastMousePos=0;var iMin=32;var grip;$.fn.TextAreaResizer=function(){return this.each(function(){textarea=$(this).addClass('processed'),staticOffset=null;$(this).wrap('
      ').parent().append($('
      ').bind("mousedown",{el:this},startDrag));var grippie=$('div.grippie',$(this).parent())[0];grippie.style.marginRight=(grippie.offsetWidth-$(this)[0].offsetWidth)+'px'})};function startDrag(e){textarea=$(e.data.el);textarea.blur();iLastMousePos=mousePosition(e).y;staticOffset=textarea.height()-iLastMousePos;textarea.css('opacity',0.25);$(document).mousemove(performDrag).mouseup(endDrag);return false}function performDrag(e){var iThisMousePos=mousePosition(e).y;var iMousePos=staticOffset+iThisMousePos;if(iLastMousePos>=(iThisMousePos)){iMousePos-=5}iLastMousePos=iThisMousePos;iMousePos=Math.max(iMin,iMousePos);textarea.height(iMousePos+'px');if(iMousePos1&&!r.visible()){t(0,true)}}).bind("search",function(){var y=(arguments.length>1)?arguments[1]:null;function z(D,C){var A;if(C&&C.length){for(var B=0;B1){y=A.slice(0,A.length-1).join(g.multipleSeparator)+g.multipleSeparator+y}y+=g.multipleSeparator}b.val(y);v();b.trigger("result",[z.data,z.value]);return true}function t(A,z){if(u==c.DEL){r.hide();return}var y=b.val();if(!z&&y==p){return}p=y;y=i(y);if(y.length>=g.minChars){b.addClass(g.loadingClass);if(!g.matchCase){y=y.toLowerCase()}f(y,k,v)}else{n();r.hide()}}function h(z){if(!z){return[""]}var A=z.split(g.multipleSeparator);var y=[];a.each(A,function(B,C){if(a.trim(C)){y[B]=a.trim(C)}});return y}function i(y){if(!g.multiple){return y}var z=h(y);return z[z.length-1]}function q(y,z){if(g.autoFill&&(i(b.val()).toLowerCase()==y.toLowerCase())&&u!=c.BACKSPACE){b.val(b.val()+z.substring(i(p).length));a.Autocompleter.Selection(l,p.length,p.length+z.length)}}function s(){clearTimeout(j);j=setTimeout(v,200)}function v(){var y=r.visible();r.hide();clearTimeout(j);n();if(g.mustMatch){b.search(function(z){if(!z){if(g.multiple){var A=h(b.val()).slice(0,-1);b.val(A.join(g.multipleSeparator)+(A.length?g.multipleSeparator:""))}else{b.val("")}}})}if(y){a.Autocompleter.Selection(l,l.value.length,l.value.length)}}function k(z,y){if(y&&y.length&&e){n();r.display(y,z);q(z,y[0].value);r.show()}else{v()}}function f(z,B,y){if(!g.matchCase){z=z.toLowerCase()}var A=m.load(z);if(A&&A.length){B(z,A)}else{if((typeof g.url=="string")&&(g.url.length>0)){var C={timestamp:+new Date()};a.each(g.extraParams,function(D,E){C[D]=typeof E=="function"?E():E});a.ajax({mode:"abort",port:"autocomplete"+l.name,dataType:g.dataType,url:g.url,data:a.extend({q:i(z),limit:g.max},C),success:function(E){var D=g.parse&&g.parse(E)||o(E);m.add(z,D);B(z,D)}})}else{r.emptyList();y(z)}}}function o(B){var y=[];var A=B.split("\n");for(var z=0;z]*)("+b.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"$1")},scroll:true,scrollHeight:180};a.Autocompleter.Cache=function(c){var f={};var d=0;function h(l,k){if(!c.matchCase){l=l.toLowerCase()}var j=l.indexOf(k);if(j==-1){return false}return j==0||c.matchContains}function g(j,i){if(d>c.cacheLength){b()}if(!f[j]){d++}f[j]=i}function e(){if(!c.data){return false}var k={},j=0;if(!c.url){c.cacheLength=1}k[""]=[];for(var m=0,l=c.data.length;m0){var o=f[j];a.each(o,function(p,k){if(h(k.value,n)){m.push(k)}})}}return m}else{if(f[n]){return f[n]}else{if(c.matchSubset){for(var l=n.length-1;l>=c.minChars;l--){var o=f[n.substr(0,l)];if(o){var m=[];a.each(o,function(p,k){if(h(k.value,n)){m[m.length]=k}});return m}}}}}return null}}};a.Autocompleter.Select=function(e,j,l,p){var i={ACTIVE:"ac_over"};var k,f=-1,r,m="",s=true,c,o;function n(){if(!s){return}c=a("
      ").hide().addClass(e.resultsClass).css("position","absolute").appendTo(document.body);o=a("
        ").appendTo(c).mouseover(function(t){if(q(t).nodeName&&q(t).nodeName.toUpperCase()=="LI"){f=a("li",o).removeClass(i.ACTIVE).index(q(t));a(q(t)).addClass(i.ACTIVE)}}).click(function(t){a(q(t)).addClass(i.ACTIVE);l();j.focus();return false}).mousedown(function(){p.mouseDownOnSelect=true}).mouseup(function(){p.mouseDownOnSelect=false});if(e.width>0){c.css("width",e.width)}s=false}function q(u){var t=u.target;while(t&&t.tagName!="LI"){t=t.parentNode}if(!t){return[]}return t}function h(t){k.slice(f,f+1).removeClass(i.ACTIVE);g(t);var v=k.slice(f,f+1).addClass(i.ACTIVE);if(e.scroll){var u=0;k.slice(0,f).each(function(){u+=this.offsetHeight});if((u+v[0].offsetHeight-o.scrollTop())>o[0].clientHeight){o.scrollTop(u+v[0].offsetHeight-o.innerHeight())}else{if(u=k.size()){f=0}}}function b(t){return e.max&&e.max").html(e.highlight(w,m)).addClass(v%2==0?"ac_even":"ac_odd").appendTo(o)[0];a.data(t,"ac_data",r[v])}k=o.find("li");if(e.selectFirst){k.slice(0,1).addClass(i.ACTIVE);f=0}if(a.fn.bgiframe){o.bgiframe()}}return{display:function(u,t){n();r=u;m=t;d()},next:function(){h(1)},prev:function(){h(-1)},pageUp:function(){if(f!=0&&f-8<0){h(-f)}else{h(-8)}},pageDown:function(){if(f!=k.size()-1&&f+8>k.size()){h(k.size()-1-f)}else{h(8)}},hide:function(){c&&c.hide();k&&k.removeClass(i.ACTIVE);f=-1},visible:function(){return c&&c.is(":visible")},current:function(){return this.visible()&&(k.filter("."+i.ACTIVE)[0]||e.selectFirst&&k[0])},show:function(){var v=a(j).offset();c.css({width:typeof e.width=="string"||e.width>0?e.width:a(j).width(),top:v.top+j.offsetHeight,left:v.left}).show();if(e.scroll){o.scrollTop(0);o.css({maxHeight:e.scrollHeight,overflow:"auto"});if(a.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var t=0;k.each(function(){t+=this.offsetHeight});var u=t>e.scrollHeight;o.css("height",u?e.scrollHeight:t);if(!u){k.width(o.width()-parseInt(k.css("padding-left"))-parseInt(k.css("padding-right")))}}}},selected:function(){var t=k&&k.filter("."+i.ACTIVE).removeClass(i.ACTIVE);return t&&t.length&&a.data(t[0],"ac_data")},emptyList:function(){o&&o.empty()},unbind:function(){c&&c.remove()}}};a.Autocompleter.Selection=function(d,e,c){if(d.setSelectionRange){d.setSelectionRange(e,c)}else{if(d.createTextRange){var b=d.createTextRange();b.collapse(true);b.moveStart("character",e);b.moveEnd("character",c);b.select()}else{if(d.selectionStart){d.selectionStart=e;d.selectionEnd=c}}}d.focus()}})(jQuery); - -var notify = function() { - var visible = false; - return { - show: function(html) { - if (html) { - $("body").css("margin-top", "2.2em"); - $(".notify span").html(html); - } - $(".notify").fadeIn("slow"); - visible = true; - }, - close: function(doPostback) { - $(".notify").fadeOut("fast"); - $("body").css("margin-top", "0"); - visible = false; - }, - isVisible: function() { return visible; } - }; -} (); - -/* - * jQuery outside events - v1.1 - 3/16/2010 - * http://benalman.com/projects/jquery-outside-events-plugin/ - * - * Copyright (c) 2010 "Cowboy" Ben Alman - * Dual licensed under the MIT and GPL licenses. - * http://benalman.com/about/license/ - */ -(function($,c,b){$.map("click dblclick mousemove mousedown mouseup mouseover mouseout change select submit keydown keypress keyup".split(" "),function(d){a(d)});a("focusin","focus"+b);a("focusout","blur"+b);$.addOutsideEvent=a;function a(g,e){e=e||g+b;var d=$(),h=g+"."+e+"-special-event";$.event.special[e]={setup:function(){d=d.add(this);if(d.length===1){$(c).bind(h,f)}},teardown:function(){d=d.not(this);if(d.length===0){$(c).unbind(h)}},add:function(i){var j=i.handler;i.handler=function(l,k){l.target=k;j.apply(this,arguments)}}};function f(i){$(d).each(function(){var j=$(this);if(this!==i.target&&!j.has(i.target).length){j.triggerHandler(e,[i.target])}})}}})(jQuery,document,"outside"); - -$(document).ready( function(){ - pickedTags().init(); - - $('input#bnewaccount').click(function() { - $('#bnewaccount').disabled=true; - }); -}); - -function yourWorkWillBeLost(e) { - if(browserTester('chrome')) { - return "Are you sure you want to leave? Your work will be lost."; - } else if(browserTester('safari')) { - return "Are you sure you want to leave? Your work will be lost."; - } else { - if(!e) e = window.event; - e.cancelBubble = true; - e.returnValue = 'If you leave, your work will be lost.'; - - if (e.stopPropagation) { - e.stopPropagation(); - e.preventDefault(); - } - return e; - } -} - -function browserTester(browserString) { - return navigator.userAgent.toLowerCase().indexOf(browserString) > -1; -} - -// Add missing IE functionality -if (!window.addEventListener) { - if (window.attachEvent) { - window.addEventListener = function (type, listener, useCapture) { - window.attachEvent('on' + type, listener); - }; - window.removeEventListener = function (type, listener, useCapture) { - window.detachEvent('on' + type, listener); - }; - } else { - window.addEventListener = function (type, listener, useCapture) { - window['on' + type] = listener; - }; - window.removeEventListener = function (type, listener, useCapture) { - window['on' + type] = null; - }; - } -} +/** + * We do not want the CSRF protection enabled for the AJAX post requests, it causes only trouble. + * Get the csrftoken cookie and pass it to the X-CSRFToken HTTP request property. + */ + +$('html').ajaxSend(function(event, xhr, settings) { + function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + try { + if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { + // Only send the token to relative URLs i.e. locally. + xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); + } + } catch (e) {} +}); + +var response_commands = { + refresh_page: function() { + window.location.reload(true) + }, + + update_post_score: function(id, inc) { + var $score_board = $('#post-' + id + '-score'); + var current = parseInt($score_board.html()) + if (isNaN(current)){ + current = 0; + } + $score_board.html(current + inc) + }, + + update_user_post_vote: function(id, vote_type) { + var $upvote_button = $('#post-' + id + '-upvote'); + var $downvote_button = $('#post-' + id + '-downvote'); + + $upvote_button.removeClass('on'); + $downvote_button.removeClass('on'); + + if (vote_type == 'up') { + $upvote_button.addClass('on'); + } else if (vote_type == 'down') { + $downvote_button.addClass('on'); + } + }, + + update_favorite_count: function(inc) { + var $favorite_count = $('#favorite-count'); + var count = parseInt($favorite_count.html()); + + if (isNaN(count)) + count = 0; + + count += inc; + + if (count == 0) + count = ''; + + $favorite_count.html(count); + }, + + update_favorite_mark: function(type) { + if (type == 'on') { + $('#favorite-mark').addClass('on'); + } else { + $('#favorite-mark').removeClass('on'); + } + }, + + mark_accepted: function(id) { + var $answer = $('#answer-container-' + id); + $answer.addClass('accepted-answer'); + $answer.find('.accept-answer').addClass('on'); + $answer.find('.accept-answer').attr('title', $answer.find('.accept-answer').attr('bn:on')); + }, + + unmark_accepted: function(id) { + var $answer = $('#answer-container-' + id); + $answer.removeClass('accepted-answer'); + $answer.find('.accept-answer').removeClass('on'); + $answer.find('.accept-answer').attr('title', $answer.find('.accept-answer').attr('bn:off')); + }, + + remove_comment: function(id) { + var $comment = $('#comment-' + id); + $comment.css('background', 'red') + $comment.fadeOut('slow', function() { + $comment.remove(); + }); + }, + + award_points: function(id) { + alert('ok'); + }, + + insert_comment: function(post_id, comment_id, comment, username, profile_url, delete_url, edit_url, convert_url, can_convert, show_latest_comments_first) { + var $container = $('#comments-container-' + post_id); + var skeleton = $('#new-comment-skeleton-' + post_id).html().toString(); + + skeleton = skeleton.replace(new RegExp('%ID%', 'g'), comment_id) + .replace(new RegExp('%COMMENT%', 'g'), comment) + .replace(new RegExp('%USERNAME%', 'g'), username) + .replace(new RegExp('%PROFILE_URL%', 'g'), profile_url) + .replace(new RegExp('%DELETE_URL%', 'g'), delete_url) + .replace(new RegExp('%EDIT_URL%', 'g'), edit_url) + .replace(new RegExp('%CONVERT_URL%', 'g'), convert_url); + if (show_latest_comments_first) { + $container.prepend(skeleton); + } else { + $container.append(skeleton); + } + + // Show the convert comment to answer tool only if the current comment can be converted + if (can_convert == true) { + $('#comment-' + comment_id + '-convert').show(); + } + + $('#comment-' + comment_id).slideDown('slow'); + }, + + update_comment: function(comment_id, comment_text) { + var $comment = $('#comment-' + comment_id); + $comment.find('.comment-text').html(comment_text); + + $comment.slideDown('slow'); + }, + + mark_deleted: function(post_type, post_id) { + if (post_type == 'question') { + var $container = $('#question-table'); + $container.addClass('deleted'); + } else { + var $el = $('#' + post_type + '-container-' + post_id); + $el.addClass('deleted'); + } + }, + + unmark_deleted: function(post_type, post_id) { + if (post_type == 'answer') { + var $answer = $('#answer-container-' + post_id); + $answer.removeClass('deleted'); + } else { + var $container = $('#question-table'); + $container.removeClass('deleted'); + } + }, + + set_subscription_button: function(text) { + $('.subscription_switch').html(text); + }, + + set_subscription_status: function(text) { + $('.subscription-status').html(text); + }, + + copy_url: function(url) { + } +} + +function show_dialog (extern) { + var default_close_function = function($diag) { + $diag.fadeOut('fast', function() { + $diag.remove(); + }); + }; + + var options = { + extra_class: '', + pos: { + x: ($(window).width() / 2) + $(window).scrollLeft(), + y: ($(window).height() / 2) + $(window).scrollTop() + }, + dim: false, + yes_text: messages.ok, + yes_callback: default_close_function, + no_text: messages.cancel, + show_no: false, + close_on_clickoutside: false, + copy: false + } + + $.extend(options, extern); + + var copy_id = ''; + if (options.copy) { + copy_id = ' id="copy_clip_button"' + } + + if (options.event != undefined && options.event.pageX != undefined && options.event.pageY != undefined) { + options.pos = {x: options.event.pageX, y: options.event.pageY}; + } else if (options.event.currentTarget != undefined) { + var el = jQuery("#" + options.event.currentTarget.id); + var position = el.offset(); + options.pos = { + x: position.left, + y: position.top + } + } + + var html = ''; + + var $dialog = $(html); + + $('body').append($dialog); + var message = $('.dialog-content')[0]; + message.style.visibility = "hidden"; + + if (options.dim === false) { + $dialog.css({ + visibility: 'hidden', + display: 'block' + }); + + options.dim = {w: $dialog.width(), h: $dialog.height()}; + + $dialog.css({ + width: 1, + height: 1, + visibility: 'visible' + }); + } + + $dialog.css({ + top: options.pos.y, + left: options.pos.x + }); + + top_position_change = (options.dim.h / 2) + left_position_change = (options.dim.w / 2) + + new_top_position = options.pos.y - top_position_change + new_left_position = options.pos.x - left_position_change + + if (new_left_position < 0) { + left_position_change = 0 + } + if (($(window).scrollTop() - new_top_position) > 0) { + top_position_change = 0 + } + if ((options.event.pageY + options.dim.h) > ($(window).height() + $(window).scrollTop())) { + top_position_change = options.dim.h + } + if ((options.event.pageX + options.dim.w) > ($(window).width() + $(window).scrollLeft())) { + left_position_change = options.dim.w + } + + $dialog.animate({ + top: "-=" + top_position_change, + left: "-=" + left_position_change, + width: options.dim.w, + height: options.dim.h + }, 200, function() { + message.style.visibility = "visible"; + }); + + $dialog.find('.dialog-yes').click(function() { + options.yes_callback($dialog); + }); + + if (options.hasOwnProperty("no_callback")) { + $dialog.find('.dialog-no:first-child').click(function() { + options.no_callback($dialog); + }); + } else { + $dialog.find('.dialog-no:first-child').click(function() { + default_close_function($dialog); + }); + } + + if (options.close_on_clickoutside) { + $dialog.one('clickoutside', function() { + default_close_function($dialog); + }); + } + + return $dialog; +} + +function show_message(evt, msg, callback) { + var $dialog = show_dialog({ + html: msg, + extra_class: 'warning', + event: evt, + yes_callback: function() { + $dialog.fadeOut('fast', function() { + $dialog.remove(); + }); + if (callback) { + callback(); + } + }, + close_on_clickoutside: true + }); +} + +function load_prompt(evt, el, url) { + $.get(url, function(data) { + var doptions = { + html: data, + extra_class: 'prompt', + yes_callback: function() { + var postvars = {}; + $dialog.find('input, textarea, select').each(function() { + postvars[$(this).attr('name')] = $(this).val(); + }); + $.post(url, postvars, function(data) { + $dialog.fadeOut('fast', function() { + $dialog.remove(); + }); + process_ajax_response(data, evt); + }, 'json'); + }, + show_no: true, + copy: false + } + + if (el.hasClass('copy')) { + $.extend(doptions, { yes_text : 'Copy', copy: true}); + } + + if (!el.is('.centered')) { + doptions.event = evt; + } + + var $dialog = show_dialog(doptions); + }); +} + +function process_ajax_response(data, evt, callback) { + if (!data.success && data['error_message'] != undefined) { + show_message(evt, data.error_message, function() {if (callback) callback(true);}); + end_command(false); + } + if (typeof data['commands'] != undefined){ + for (var command in data.commands) { + response_commands[command].apply(null, data.commands[command]) + + + } + + if (data['message'] != undefined) { + show_message(evt, data.message, function() {if (callback) callback(false);}) + } else { + if (callback) callback(false); + } + end_command(true); + } +} + +var running = false; + +function start_command() { + $('body').append($('
        ')); + running = true; +} + +function end_command(success) { + if (success) { + $('#command-loader').addClass('success'); + $('#command-loader').fadeOut("slow", function() { + $('#command-loader').remove(); + running = false; + }); + } else { + $('#command-loader').remove(); + running = false; + } +} + +var comment_box_cursor_position = 0; +function canned_comment(post_id, comment) { + textarea = $('#comment-' + post_id + '-form textarea') + + // Get the text from the beginning to the caret + textarea_start = textarea.val().substr(0, comment_box_cursor_position) + + // Get the text from the caret to the end + textarea_end = textarea.val().substr(comment_box_cursor_position, textarea.val().length) + + textarea.val(textarea_start + comment + textarea_end); +} + +$(function() { + $('textarea.commentBox').bind('keydown keyup mousedown mouseup mousemove', function(evt) { + comment_box_cursor_position = $(this).caret().start; + }); + + $('textarea.commentBox').blur(function() { + //alert(comment_box_cursor_position); + }); + + $('a.ajax-command').live('click', function(evt) { + if (running) return false; + + var el = $(this); + + var ajax_url = el.attr('href') + ajax_url = ajax_url + "?nocache=" + new Date().getTime() + + $('.context-menu-dropdown').slideUp('fast'); + + if (el.is('.withprompt')) { + load_prompt(evt, el, ajax_url); + } else if(el.is('.confirm')) { + var doptions = { + html: messages.confirm, + extra_class: 'confirm', + yes_callback: function() { + start_command(); + $.getJSON(ajax_url, function(data) { + process_ajax_response(data, evt); + $dialog.fadeOut('fast', function() { + $dialog.remove(); + }); + }); + }, + yes_text: messages.yes, + show_no: true, + no_text: messages.no + } + + if (!el.is('.centered')) { + doptions.event = evt; + } + var $dialog = show_dialog(doptions); + } else { + start_command(); + $.ajax({ + url: ajax_url, + type: "POST", + dataType: "json", + contentType: "application/json; charset=utf-8", + success: function(data) { + process_ajax_response(data, evt); + } + }); + } + + return false + }); + + $('.context-menu').each(function() { + var $menu = $(this); + var $trigger = $menu.find('.context-menu-trigger'); + var $dropdown = $menu.find('.context-menu-dropdown'); + + $trigger.click(function() { + $dropdown.slideToggle('fast', function() { + if ($dropdown.is(':visible')) { + $dropdown.one('clickoutside', function() { + if ($dropdown.is(':visible')) + $dropdown.slideUp('fast'); + }); + } + }); + }); + }); + + $('div.comment-form-container').each(function() { + var $container = $(this); + var $comment_tools = $container.parent().find('.comment-tools'); + var $comments_container = $container.parent().find('.comments-container'); + + var $form = $container.find('form'); + + if ($form.length) { + var $textarea = $container.find('textarea'); + var textarea = $textarea.get(0); + var $button = $container.find('.comment-submit'); + var $cancel = $container.find('.comment-cancel'); + var $chars_left_message = $container.find('.comments-chars-left-msg'); + var $chars_togo_message = $container.find('.comments-chars-togo-msg'); + var $chars_counter = $container.find('.comments-char-left-count'); + + var $add_comment_link = $comment_tools.find('.add-comment-link'); + + var chars_limits = $chars_counter.html().split('|'); + + var min_length = parseInt(chars_limits[0]); + var max_length = parseInt(chars_limits[1]); + + var warn_length = max_length - 30; + var current_length = 0; + var comment_in_form = false; + var interval = null; + + var hcheck = !($.browser.msie || $.browser.opera); + + $textarea.css("padding-top", 0).css("padding-bottom", 0).css("resize", "none"); + textarea.style.overflow = 'hidden'; + + + function cleanup_form() { + $textarea.val(''); + $textarea.css('height', 80); + $chars_counter.html(max_length); + $chars_left_message.removeClass('warn'); + comment_in_form = false; + current_length = 0; + + $chars_left_message.hide(); + $chars_togo_message.show(); + + $chars_counter.removeClass('warn'); + $chars_counter.html(min_length); + $button.attr("disabled","disabled"); + + interval = null; + } + + cleanup_form(); + + function process_form_changes() { + var length = $textarea.val().replace(/[ ]{2,}/g," ").length; + + if (current_length == length) + return; + + if (length < warn_length && current_length >= warn_length) { + $chars_counter.removeClass('warn'); + } else if (current_length < warn_length && length >= warn_length){ + $chars_counter.addClass('warn'); + } + + if (length < min_length) { + $chars_left_message.hide(); + $chars_togo_message.show(); + $chars_counter.html(min_length - length); + } else { + length = $textarea.val().length; + $chars_togo_message.hide(); + $chars_left_message.show(); + $chars_counter.html(max_length - length); + } + + if (length > max_length || length < min_length) { + $button.attr("disabled","disabled"); + } else { + $button.removeAttr("disabled"); + } + + var current_height = textarea.style.height; + if (hcheck) + textarea.style.height = "0px"; + + var h = Math.max(80, textarea.scrollHeight); + textarea.style.height = current_height; + $textarea.animate({height: h + 'px'}, 50); + + current_length = length; + } + + function show_comment_form() { + $container.slideDown('slow'); + $add_comment_link.fadeOut('slow'); + $textarea.focus(); + interval = window.setInterval(function() { + process_form_changes(); + }, 200); + } + + function hide_comment_form() { + if (interval != null) { + window.clearInterval(interval); + interval = null; + } + $container.slideUp('slow'); + $add_comment_link.fadeIn('slow'); + } + + $add_comment_link.click(function(){ + cleanup_form(); + show_comment_form(); + return false; + }); + + $('#' + $comments_container.attr('id') + ' .comment-edit').live('click', function() { + var $link = $(this); + var comment_id = /comment-(\d+)-edit/.exec($link.attr('id'))[1]; + var $comment = $('#comment-' + comment_id); + + comment_in_form = comment_id; + + $.get($link.attr('href'), function(data) { + $textarea.val(data); + }); + + $comment.slideUp('slow'); + show_comment_form(); + return false; + }); + + $button.click(function(evt) { + if (running) return false; + + var post_data = { + comment: $textarea.val() + } + + if (comment_in_form) { + post_data['id'] = comment_in_form; + } + + start_command(); + $.post($form.attr('action'), post_data, function(data) { + process_ajax_response(data, evt, function(error) { + if (!error) { + cleanup_form(); + hide_comment_form(); + } + }); + + }, "json"); + + return false; + }); + + // Submit comment with CTRL + Enter + $textarea.keydown(function(e) { + if (e.ctrlKey && e.keyCode == 13 && !$button.attr('disabled')) { + // console.log('submit'); + $(this).parent().find('input.comment-submit').click(); + } + }); + + $cancel.click(function(event) { + if (confirm("You will lose all of your changes in this comment. Do you still wish to proceed?")){ + if (comment_in_form) { + $comment = $('#comment-' + comment_in_form).slideDown('slow'); + } + hide_comment_form(); + cleanup_form(); + } + return false; + }); + } + + $comment_tools.find('.show-all-comments-link').click(function() { + $comments_container.find('.not_top_scorer').slideDown('slow'); + $(this).fadeOut('slow'); + $comment_tools.find('.comments-showing').fadeOut('slow'); + return false; + }); + }); + + if ($('#editor').length) { + var $editor = $('#editor'); + var $previewer = $('#previewer'); + var $container = $('#editor-metrics'); + + var initial_whitespace_rExp = /^[^A-Za-zА-Яа-я0-9]+/gi; + var non_alphanumerics_rExp = rExp = /[^A-Za-zА-Яа-я0-9]+/gi; + var editor_interval = null; + + $editor.focus(function() { + if (editor_interval == null) { + editor_interval = window.setInterval(function() { + recalc_metrics(); + }, 200); + } + }); + + function recalc_metrics() { + var text = $previewer.text(); + + var char_count = text.length; + var fullStr = text + " "; + var left_trimmedStr = fullStr.replace(initial_whitespace_rExp, ""); + var cleanedStr = left_trimmedStr.replace(non_alphanumerics_rExp, " "); + var splitString = cleanedStr.split(" "); + var word_count = splitString.length - 1; + + var metrics = char_count + " " + (char_count == 1 ? messages.character : messages.characters); + metrics += " / " + word_count + " " + (word_count == 1 ? messages.word : messages.words); + $container.html(metrics); + } + } +}); + +//var scriptUrl, interestingTags, ignoredTags, tags, $; +function pickedTags(){ + + var sendAjax = function(tagname, reason, action, callback){ + var url = scriptUrl; + if (action == 'add'){ + url += $.i18n._('mark-tag/'); + if (reason == 'good'){ + url += $.i18n._('interesting/'); + } + else { + url += $.i18n._('ignored/'); + } + } + else { + url += $.i18n._('unmark-tag/'); + } + url = url + tagname + '/'; + + var call_settings = { + type:'POST', + url:url, + data: '' + }; + if (callback !== false){ + call_settings.success = callback; + } + $.ajax(call_settings); + }; + + + var unpickTag = function(from_target ,tagname, reason, send_ajax){ + //send ajax request to delete tag + var deleteTagLocally = function(){ + from_target[tagname].remove(); + delete from_target[tagname]; + $(".tags.interesting").trigger('contentchanged'); + }; + + if (send_ajax){ + sendAjax(tagname,reason,'remove',deleteTagLocally); + } + else { + deleteTagLocally(); + } + }; + + var setupTagDeleteEvents = function(obj,tag_store,tagname,reason,send_ajax){ + obj.unbind('mouseover').bind('mouseover', function(){ + $(this).attr('src', mediaUrl('media/images/close-small-hover.png')); + }); + obj.unbind('mouseout').bind('mouseout', function(){ + $(this).attr('src', mediaUrl('media/images/close-small-dark.png')); + }); + obj.click( function(){ + unpickTag(tag_store,tagname,reason,send_ajax); + }); + }; + + var handlePickedTag = function(obj,reason){ + var tagname = $.trim($(obj).prev().attr('value')); + var to_target = interestingTags; + var from_target = ignoredTags; + var to_tag_container; + if (reason == 'bad'){ + to_target = ignoredTags; + from_target = interestingTags; + to_tag_container = $('div .tags.ignored'); + } + else if (reason != 'good'){ + return; + } + else { + to_tag_container = $('div .tags.interesting'); + } + + if (tagname in from_target){ + unpickTag(from_target,tagname,reason,false); + } + + if (!(tagname in to_target)){ + //send ajax request to pick this tag + + sendAjax(tagname,reason,'add',function(){ + var new_tag = $(''); + new_tag.addClass('deletable-tag'); + var tag_link = $(''); + tag_link.attr('rel','tag'); + tag_link.attr('href', scriptUrl + $.i18n._('tags/') + tagname + '/'); + tag_link.html(tagname); + var del_link = $(''); + del_link.addClass('delete-icon'); + del_link.attr('src', mediaUrl('media/images/close-small-dark.png')); + + setupTagDeleteEvents(del_link, to_target, tagname, reason, true); + + new_tag.append(tag_link); + new_tag.append(del_link); + to_tag_container.append(new_tag); + + to_target[tagname] = new_tag; + + to_tag_container.trigger('contentchanged'); + }); + } + }; + + var collectPickedTags = function(){ + var good_prefix = 'interesting-tag-'; + var bad_prefix = 'ignored-tag-'; + var good_re = RegExp('^' + good_prefix); + var bad_re = RegExp('^' + bad_prefix); + interestingTags = {}; + ignoredTags = {}; + $('.deletable-tag').each( + function(i,item){ + var item_id = $(item).attr('id'); + var tag_name, tag_store; + if (good_re.test(item_id)){ + tag_name = item_id.replace(good_prefix,''); + tag_store = interestingTags; + reason = 'good'; + } + else if (bad_re.test(item_id)){ + tag_name = item_id.replace(bad_prefix,''); + tag_store = ignoredTags; + reason = 'bad'; + } + else { + return; + } + tag_store[tag_name] = $(item); + setupTagDeleteEvents($(item).find('img'),tag_store,tag_name,reason,true); + } + ); + }; + + var setupHideIgnoredQuestionsControl = function(){ + $('#hideIgnoredTagsCb').unbind('click').click(function(){ + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + url: scriptUrl + $.i18n._('command/'), + data: {command:'toggle-ignored-questions'} + }); + }); + }; + return { + init: function(){ + collectPickedTags(); + setupHideIgnoredQuestionsControl(); + $("#interestingTagInput, #ignoredTagInput").autocomplete(messages.matching_tags_url, { + minChars: 1, + matchContains: true, + max: 20, + /*multiple: false, - the favorite tags and ignore tags don't let you do multiple tags + multipleSeparator: " "*/ + + formatItem: function(row, i, max, value) { + return row[1] + " (" + row[2] + ")"; + }, + + formatResult: function(row, i, max, value){ + return row[1]; + } + + }); + $("#interestingTagAdd").click(function(){handlePickedTag(this,'good');}); + $("#ignoredTagAdd").click(function(){handlePickedTag(this,'bad');}); + } + }; +} + +Hilite={elementid:"content",exact:true,max_nodes:1000,onload:true,style_name:"hilite",style_name_suffix:true,debug_referrer:""};Hilite.search_engines=[["local","q"],["cnprog\\.","q"],["google\\.","q"],["search\\.yahoo\\.","p"],["search\\.msn\\.","q"],["search\\.live\\.","query"],["search\\.aol\\.","userQuery"],["ask\\.com","q"],["altavista\\.","q"],["feedster\\.","q"],["search\\.lycos\\.","q"],["alltheweb\\.","q"],["technorati\\.com/search/([^\\?/]+)",1],["dogpile\\.com/info\\.dogpl/search/web/([^\\?/]+)",1,true]];Hilite.decodeReferrer=function(d){var g=null;var e=new RegExp("");for(var c=0;c2&&f[2]){a=decodeURIComponent(a)}a=a.replace(/\'|"/g,"");a=a.split(/[\s,\+\.]+/);return a}break}}return null};Hilite.decodeReferrerQS=function(f,d){var b=f.indexOf("?");var c;if(b>=0){var a=new String(f.substring(b+1));b=0;c=0;while((b>=0)&&((c=a.indexOf("=",b))>=0)){var e,g;e=a.substring(b,c);b=a.indexOf("&",c)+1;if(e==d){if(b<=0){return a.substring(c+1)}else{return a.substring(c+1,b-1)}}else{if(b<=0){return null}}}}return null};Hilite.hiliteElement=function(f,e){if(!e||f.childNodes.length==0){return}var c=new Array();for(var b=0;b0){c++;if(c>=Hilite.max_nodes){var b=function(){Hilite.walkElements(d,f,e)};setTimeout(b,50);return}if(d.nodeType==1){if(!a.test(d.tagName)&&d.childNodes.length>0){d=d.childNodes[0];f++;continue}}else{if(d.nodeType==3){d=e(d)}}if(d.nextSibling){d=d.nextSibling}else{while(f>0){d=d.parentNode;f--;if(d.nextSibling){d=d.nextSibling;break}}}}};if(Hilite.onload){if(window.attachEvent){window.attachEvent("onload",Hilite.hilite)}else{if(window.addEventListener){window.addEventListener("load",Hilite.hilite,false)}else{var __onload=window.onload;window.onload=function(){Hilite.hilite();__onload()}}}}; + +var mediaUrl = function(resource){ + return scriptUrl + 'm/' + osqaSkin + '/' + resource; +}; + +/* + * jQuery i18n plugin + * @requires jQuery v1.1 or later + * + * Examples at: http://recurser.com/articles/2008/02/21/jquery-i18n-translation-plugin/ + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Based on 'javascript i18n that almost doesn't suck' by markos + * http://markos.gaivo.net/blog/?p=100 + * + * Revision: $Id$ + * Version: 1.0.0 Feb-10-2008 + */ + (function($) { +/** + * i18n provides a mechanism for translating strings using a jscript dictionary. + * + */ + + +/* + * i18n property list + */ +$.i18n = { + +/** + * setDictionary() + * Initialise the dictionary and translate nodes + * + * @param property_list i18n_dict : The dictionary to use for translation + */ + setDictionary: function(i18n_dict) { + i18n_dict = i18n_dict; + }, + +/** + * _() + * The actual translation function. Looks the given string up in the + * dictionary and returns the translation if one exists. If a translation + * is not found, returns the original word + * + * @param string str : The string to translate + * @param property_list params : params for using printf() on the string + * @return string : Translated word + * + */ + _: function (str, params) { + var transl = str; + if (i18n_dict&& i18n_dict[str]) { + transl = i18n_dict[str]; + } + return this.printf(transl, params); + }, + +/** + * toEntity() + * Change non-ASCII characters to entity representation + * + * @param string str : The string to transform + * @return string result : Original string with non-ASCII content converted to entities + * + */ + toEntity: function (str) { + var result = ''; + for (var i=0;i 128) + result += "&#"+str.charCodeAt(i)+";"; + else + result += str.charAt(i); + } + return result; + }, + +/** + * stripStr() + * + * @param string str : The string to strip + * @return string result : Stripped string + * + */ + stripStr: function(str) { + return str.replace(/^\s*/, "").replace(/\s*$/, ""); + }, + +/** + * stripStrML() + * + * @param string str : The multi-line string to strip + * @return string result : Stripped string + * + */ + stripStrML: function(str) { + // Split because m flag doesn't exist before JS1.5 and we need to + // strip newlines anyway + var parts = str.split('\n'); + for (var i=0; i15 points requried to upvote':'??+15?????????', + '>100 points required to downvote':'??+100?????????', + 'please see': '??', + 'cannot vote for own posts':'??????????', + 'daily vote cap exhausted':'????????????????', + 'cannot revoke old vote':'??????????????', + 'please confirm offensive':"??????????????????????", + 'anonymous users cannot flag offensive posts':'???????????', + 'cannot flag message as offensive twice':'???????', + 'flag offensive cap exhausted':'?????????????5?‘??’???', + 'need >15 points to report spam':"??+15??????‘???’?", + 'confirm delete':"?????/????????", + 'anonymous users cannot delete/undelete':"???????????????", + 'post recovered':"?????????????", + 'post deleted':"????????????", + 'add comment':'????', + 'community karma points':'????', + 'to comment, need':'????', + 'delete this comment':'?????', + 'hide comments':"????", + 'add a comment':"????", + 'comments':"??", + 'confirm delete comment':"?????????", + 'characters':'??', + 'can write':'???', + 'click to close':'???????', + 'loading...':'???...', + 'tags cannot be empty':'???????', + 'tablimits info':"??5????????????20????", + 'content cannot be empty':'???????', + 'content minchars': '????? {0} ???', + 'please enter title':'??????', + 'title minchars':"????? {0} ???", + 'delete':'??', + 'undelete': '??', + 'bold':'??', + 'italic':'??', + 'link':'???', + 'quote':'??', + 'preformatted text':'??', + 'image':'??', + 'numbered list':'??????', + 'bulleted list':'??????', + 'heading':'??', + 'horizontal bar':'???', + 'undo':'??', + 'redo':'??', + 'enter image url':'??????

        ???
        http://www.example.com/image.jpg \"????\"', + 'enter url':'??Web??

        ???
        http://www.cnprog.com/ \"????\"

        "', + 'upload image':'?????????' +}; + +var i18nEn = { + 'need >15 points to report spam':'need >15 points to report spam ', + '>15 points requried to upvote':'>15 points required to upvote ', + 'tags cannot be empty':'please enter at least one tag', + 'anonymous users cannot vote':'sorry, anonymous users cannot vote ', + 'anonymous users cannot select favorite questions':'sorry, anonymous users cannot select favorite questions ', + 'to comment, need': '(to comment other people\'s posts, karma ', + 'please see':'please see ', + 'community karma points':' or more is necessary) - ', + 'upload image':'Upload image:', + 'enter image url':'enter URL of the image, e.g. http://www.example.com/image.jpg \"image title\"', + 'enter url':'enter Web address, e.g. http://www.example.com \"page title\"', + 'daily vote cap exhausted':'sorry, you\'ve used up todays vote cap', + 'cannot pick own answer as best':'sorry, you cannot accept your own answer', + 'cannot revoke old vote':'sorry, older votes cannot be revoked', + 'please confirm offensive':'are you sure this post is offensive, contains spam, advertising, malicious remarks, etc.?', + 'flag offensive cap exhausted':'sorry, you\'ve used up todays cap of flagging offensive messages ', + 'confirm delete':'are you sure you want to delete this?', + 'anonymous users cannot delete/undelete':'sorry, anonymous users cannot delete or undelete posts', + 'post recovered':'your post is now restored!', + 'post deleted':'your post has been deleted', + 'confirm delete comment':'do you really want to delete this comment?', + 'can write':'have ', + 'tablimits info':'up to 5 tags, no more than 20 characters each', + 'content minchars': 'please enter more than {0} characters', + 'title minchars':"please enter at least {0} characters", + 'characters':'characters left', + 'cannot vote for own posts':'sorry, you cannot vote for your own posts', + 'cannot flag message as offensive twice':'cannot flag message as offensive twice ', + '>100 points required to downvote':'>100 points required to downvote ' +}; + +var i18nEs = { + 'insufficient privilege':'privilegio insuficiente', + 'cannot pick own answer as best':'no puede escoger su propia respuesta como la mejor', + 'anonymous users cannot select favorite questions':'usuarios anonimos no pueden seleccionar', + 'please login':'por favor inicie sesión', + 'anonymous users cannot vote':'usuarios anónimos no pueden votar', + '>15 points requried to upvote': '>15 puntos requeridos para votar positivamente', + '>100 points required to downvote':'>100 puntos requeridos para votar negativamente', + 'please see': 'por favor vea', + 'cannot vote for own posts':'no se puede votar por sus propias publicaciones', + 'daily vote cap exhausted':'cuota de votos diarios excedida', + 'cannot revoke old vote':'no puede revocar un voto viejo', + 'please confirm offensive':"por favor confirme ofensiva", + 'anonymous users cannot flag offensive posts':'usuarios anónimos no pueden marcar publicaciones como ofensivas', + 'cannot flag message as offensive twice':'no puede marcar mensaje como ofensivo dos veces', + 'flag offensive cap exhausted':'cuota para marcar ofensivas ha sido excedida', + 'need >15 points to report spam':"necesita >15 puntos para reportar spam", + 'confirm delete':"¿Está seguro que desea borrar esto?", + 'anonymous users cannot delete/undelete':"usuarios anónimos no pueden borrar o recuperar publicaciones", + 'post recovered':"publicación recuperada", + 'post deleted':"publicación borrada?", + 'add comment':'agregar comentario', + 'community karma points':'reputación comunitaria', + 'to comment, need':'para comentar, necesita reputación', + 'delete this comment':'borrar este comentario', + 'hide comments':"ocultar comentarios", + 'add a comment':"agregar comentarios", + 'comments':"comentarios", + 'confirm delete comment':"¿Realmente desea borrar este comentario?", + 'characters':'caracteres faltantes', + 'can write':'tiene ', + 'click to close':'haga click para cerrar', + 'loading...':'cargando...', + 'tags cannot be empty':'las etiquetas no pueden estar vacías', + 'tablimits info':"hasta 5 etiquetas de no mas de 20 caracteres cada una", + 'content cannot be empty':'el contenido no puede estar vacío', + 'content minchars': 'por favor introduzca mas de {0} caracteres', + 'please enter title':'por favor ingrese un título', + 'title minchars':"por favor introduzca al menos {0} caracteres", + 'delete':'borrar', + 'undelete': 'recuperar', + 'bold': 'negrita', + 'italic':'cursiva', + 'link':'enlace', + 'quote':'citar', + 'preformatted text':'texto preformateado', + 'image':'imagen', + 'numbered list':'lista numerada', + 'bulleted list':'lista no numerada', + 'heading':'??', + 'horizontal bar':'barra horizontal', + 'undo':'deshacer', + 'redo':'rehacer', + 'enter image url':'introduzca la URL de la imagen, por ejemplo?
        http://www.example.com/image.jpg \"titulo de imagen\"', + 'enter url':'introduzca direcciones web, ejemplo?
        http://www.cnprog.com/ \"titulo del enlace\"

        "', + 'upload image':'cargar imagen?', + 'questions/' : 'preguntas/', + 'vote/' : 'votar/' +}; + +var i18n = { + 'en':i18nEn, + 'zh_CN':i18nZh, + 'es':i18nEs +}; + +var i18n_dict = i18n[i18nLang]; + +/* + jQuery TextAreaResizer plugin + Created on 17th January 2008 by Ryan O'Dell + Version 1.0.4 +*/(function($){var textarea,staticOffset;var iLastMousePos=0;var iMin=32;var grip;$.fn.TextAreaResizer=function(){return this.each(function(){textarea=$(this).addClass('processed'),staticOffset=null;$(this).wrap('
        ').parent().append($('
        ').bind("mousedown",{el:this},startDrag));var grippie=$('div.grippie',$(this).parent())[0];grippie.style.marginRight=(grippie.offsetWidth-$(this)[0].offsetWidth)+'px'})};function startDrag(e){textarea=$(e.data.el);textarea.blur();iLastMousePos=mousePosition(e).y;staticOffset=textarea.height()-iLastMousePos;textarea.css('opacity',0.25);$(document).mousemove(performDrag).mouseup(endDrag);return false}function performDrag(e){var iThisMousePos=mousePosition(e).y;var iMousePos=staticOffset+iThisMousePos;if(iLastMousePos>=(iThisMousePos)){iMousePos-=5}iLastMousePos=iThisMousePos;iMousePos=Math.max(iMin,iMousePos);textarea.height(iMousePos+'px');if(iMousePos1&&!r.visible()){t(0,true)}}).bind("search",function(){var y=(arguments.length>1)?arguments[1]:null;function z(D,C){var A;if(C&&C.length){for(var B=0;B1){y=A.slice(0,A.length-1).join(g.multipleSeparator)+g.multipleSeparator+y}y+=g.multipleSeparator}b.val(y);v();b.trigger("result",[z.data,z.value]);return true}function t(A,z){if(u==c.DEL){r.hide();return}var y=b.val();if(!z&&y==p){return}p=y;y=i(y);if(y.length>=g.minChars){b.addClass(g.loadingClass);if(!g.matchCase){y=y.toLowerCase()}f(y,k,v)}else{n();r.hide()}}function h(z){if(!z){return[""]}var A=z.split(g.multipleSeparator);var y=[];a.each(A,function(B,C){if(a.trim(C)){y[B]=a.trim(C)}});return y}function i(y){if(!g.multiple){return y}var z=h(y);return z[z.length-1]}function q(y,z){if(g.autoFill&&(i(b.val()).toLowerCase()==y.toLowerCase())&&u!=c.BACKSPACE){b.val(b.val()+z.substring(i(p).length));a.Autocompleter.Selection(l,p.length,p.length+z.length)}}function s(){clearTimeout(j);j=setTimeout(v,200)}function v(){var y=r.visible();r.hide();clearTimeout(j);n();if(g.mustMatch){b.search(function(z){if(!z){if(g.multiple){var A=h(b.val()).slice(0,-1);b.val(A.join(g.multipleSeparator)+(A.length?g.multipleSeparator:""))}else{b.val("")}}})}if(y){a.Autocompleter.Selection(l,l.value.length,l.value.length)}}function k(z,y){if(y&&y.length&&e){n();r.display(y,z);q(z,y[0].value);r.show()}else{v()}}function f(z,B,y){if(!g.matchCase){z=z.toLowerCase()}var A=m.load(z);if(A&&A.length){B(z,A)}else{if((typeof g.url=="string")&&(g.url.length>0)){var C={timestamp:+new Date()};a.each(g.extraParams,function(D,E){C[D]=typeof E=="function"?E():E});a.ajax({mode:"abort",port:"autocomplete"+l.name,dataType:g.dataType,url:g.url,data:a.extend({q:i(z),limit:g.max},C),success:function(E){var D=g.parse&&g.parse(E)||o(E);m.add(z,D);B(z,D)}})}else{r.emptyList();y(z)}}}function o(B){var y=[];var A=B.split("\n");for(var z=0;z]*)("+b.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"$1")},scroll:true,scrollHeight:180};a.Autocompleter.Cache=function(c){var f={};var d=0;function h(l,k){if(!c.matchCase){l=l.toLowerCase()}var j=l.indexOf(k);if(j==-1){return false}return j==0||c.matchContains}function g(j,i){if(d>c.cacheLength){b()}if(!f[j]){d++}f[j]=i}function e(){if(!c.data){return false}var k={},j=0;if(!c.url){c.cacheLength=1}k[""]=[];for(var m=0,l=c.data.length;m0){var o=f[j];a.each(o,function(p,k){if(h(k.value,n)){m.push(k)}})}}return m}else{if(f[n]){return f[n]}else{if(c.matchSubset){for(var l=n.length-1;l>=c.minChars;l--){var o=f[n.substr(0,l)];if(o){var m=[];a.each(o,function(p,k){if(h(k.value,n)){m[m.length]=k}});return m}}}}}return null}}};a.Autocompleter.Select=function(e,j,l,p){var i={ACTIVE:"ac_over"};var k,f=-1,r,m="",s=true,c,o;function n(){if(!s){return}c=a("
        ").hide().addClass(e.resultsClass).css("position","absolute").appendTo(document.body);o=a("
          ").appendTo(c).mouseover(function(t){if(q(t).nodeName&&q(t).nodeName.toUpperCase()=="LI"){f=a("li",o).removeClass(i.ACTIVE).index(q(t));a(q(t)).addClass(i.ACTIVE)}}).click(function(t){a(q(t)).addClass(i.ACTIVE);l();j.focus();return false}).mousedown(function(){p.mouseDownOnSelect=true}).mouseup(function(){p.mouseDownOnSelect=false});if(e.width>0){c.css("width",e.width)}s=false}function q(u){var t=u.target;while(t&&t.tagName!="LI"){t=t.parentNode}if(!t){return[]}return t}function h(t){k.slice(f,f+1).removeClass(i.ACTIVE);g(t);var v=k.slice(f,f+1).addClass(i.ACTIVE);if(e.scroll){var u=0;k.slice(0,f).each(function(){u+=this.offsetHeight});if((u+v[0].offsetHeight-o.scrollTop())>o[0].clientHeight){o.scrollTop(u+v[0].offsetHeight-o.innerHeight())}else{if(u=k.size()){f=0}}}function b(t){return e.max&&e.max").html(e.highlight(w,m)).addClass(v%2==0?"ac_even":"ac_odd").appendTo(o)[0];a.data(t,"ac_data",r[v])}k=o.find("li");if(e.selectFirst){k.slice(0,1).addClass(i.ACTIVE);f=0}if(a.fn.bgiframe){o.bgiframe()}}return{display:function(u,t){n();r=u;m=t;d()},next:function(){h(1)},prev:function(){h(-1)},pageUp:function(){if(f!=0&&f-8<0){h(-f)}else{h(-8)}},pageDown:function(){if(f!=k.size()-1&&f+8>k.size()){h(k.size()-1-f)}else{h(8)}},hide:function(){c&&c.hide();k&&k.removeClass(i.ACTIVE);f=-1},visible:function(){return c&&c.is(":visible")},current:function(){return this.visible()&&(k.filter("."+i.ACTIVE)[0]||e.selectFirst&&k[0])},show:function(){var v=a(j).offset();c.css({width:typeof e.width=="string"||e.width>0?e.width:a(j).width(),top:v.top+j.offsetHeight,left:v.left}).show();if(e.scroll){o.scrollTop(0);o.css({maxHeight:e.scrollHeight,overflow:"auto"});if(a.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var t=0;k.each(function(){t+=this.offsetHeight});var u=t>e.scrollHeight;o.css("height",u?e.scrollHeight:t);if(!u){k.width(o.width()-parseInt(k.css("padding-left"))-parseInt(k.css("padding-right")))}}}},selected:function(){var t=k&&k.filter("."+i.ACTIVE).removeClass(i.ACTIVE);return t&&t.length&&a.data(t[0],"ac_data")},emptyList:function(){o&&o.empty()},unbind:function(){c&&c.remove()}}};a.Autocompleter.Selection=function(d,e,c){if(d.setSelectionRange){d.setSelectionRange(e,c)}else{if(d.createTextRange){var b=d.createTextRange();b.collapse(true);b.moveStart("character",e);b.moveEnd("character",c);b.select()}else{if(d.selectionStart){d.selectionStart=e;d.selectionEnd=c}}}d.focus()}})(jQuery); + +var notify = function() { + var visible = false; + return { + show: function(html) { + if (html) { + $("body").css("margin-top", "2.2em"); + $(".notify span").html(html); + } + $(".notify").fadeIn("slow"); + visible = true; + }, + close: function(doPostback) { + $(".notify").fadeOut("fast"); + $("body").css("margin-top", "0"); + visible = false; + }, + isVisible: function() { return visible; } + }; +} (); + +/* + * jQuery outside events - v1.1 - 3/16/2010 + * http://benalman.com/projects/jquery-outside-events-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ +(function($,c,b){$.map("click dblclick mousemove mousedown mouseup mouseover mouseout change select submit keydown keypress keyup".split(" "),function(d){a(d)});a("focusin","focus"+b);a("focusout","blur"+b);$.addOutsideEvent=a;function a(g,e){e=e||g+b;var d=$(),h=g+"."+e+"-special-event";$.event.special[e]={setup:function(){d=d.add(this);if(d.length===1){$(c).bind(h,f)}},teardown:function(){d=d.not(this);if(d.length===0){$(c).unbind(h)}},add:function(i){var j=i.handler;i.handler=function(l,k){l.target=k;j.apply(this,arguments)}}};function f(i){$(d).each(function(){var j=$(this);if(this!==i.target&&!j.has(i.target).length){j.triggerHandler(e,[i.target])}})}}})(jQuery,document,"outside"); + +$(document).ready( function(){ + pickedTags().init(); + + $('input#bnewaccount').click(function() { + $('#bnewaccount').disabled=true; + }); +}); + +function yourWorkWillBeLost(e) { + if(browserTester('chrome')) { + return "Are you sure you want to leave? Your work will be lost."; + } else if(browserTester('safari')) { + return "Are you sure you want to leave? Your work will be lost."; + } else { + if(!e) e = window.event; + e.cancelBubble = true; + e.returnValue = 'If you leave, your work will be lost.'; + + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + return e; + } +} + +function browserTester(browserString) { + return navigator.userAgent.toLowerCase().indexOf(browserString) > -1; +} + +// Add missing IE functionality +if (!window.addEventListener) { + if (window.attachEvent) { + window.addEventListener = function (type, listener, useCapture) { + window.attachEvent('on' + type, listener); + }; + window.removeEventListener = function (type, listener, useCapture) { + window.detachEvent('on' + type, listener); + }; + } else { + window.addEventListener = function (type, listener, useCapture) { + window['on' + type] = listener; + }; + window.removeEventListener = function (type, listener, useCapture) { + window['on' + type] = null; + }; + } +} diff --git a/forum/skins/default/media/js/viewbox.css b/forum/skins/default/media/js/viewbox.css index b563176..ad722b2 100644 --- a/forum/skins/default/media/js/viewbox.css +++ b/forum/skins/default/media/js/viewbox.css @@ -1,137 +1,137 @@ -/* Miniatury */ -.thumbdiv { - float:left; - position:relative; -} -.thumbdiv .title { - position:absolute; - padding:2px; - font-size:1.0em; - color:#fff; - background:#000; - border-top:solid 1px #000; - opacity:0.8; - bottom:14px; - left:8px; - right:9px; -} -/* VIEWBOX */ -#viewbox { - position:fixed; - height:100%; - width:100%; - top:0; - left:0; - background:#383739; - z-index:2190; - overflow:auto; -} -.vb_wrap { - position:absolute; - padding:10px; - height:20px; - width:20px; - background-image:url(../images/viewbox/ViewBox_bg.png); - background-color:#0f0e0e; - overflow:visible; - z-index:2200; - margin:0 auto; - top:0; - left:0; - border-radius:10px; - box-shadow: 4px 4px 7px #000; -} -.vb_wrap .content { - position:relative; - overflow:visible; -} -.vb_wrap .number { - position:absolute; - top:-32px; - left:1px; - font-size:0.9em; - color:#0c0c0c; -} -.vb_wrap .close { - position:absolute; - top:-40px; - right:-7px; - background:url(../images/viewbox/viewbox_close.png) top left no-repeat; - height:30px; - width:30px; -} -* html .vb_wrap .close { - top:-26px; - right:7px; -} -.vb_wrap .next { - position:absolute; - right:-60px; - background:url(../images/viewbox/ViewBox_next.png) top left no-repeat; - height:50px; - width:50px; - z-index:2190; -} -.vb_wrap .prev { - position:absolute; - left:-59px; - background:url(../images/viewbox/ViewBox_prev.png) top right no-repeat; - height:50px; - width:50px; - z-index:2190; -} -.vb_wrap .close a, .vb_wrap .next a, .vb_wrap .prev a { - display:block; - height:100%; - width:100%; - text-indent:-777em; - outline:none; -} -.vb_wrap h1 { - margin:5px 0px 8px; - padding-bottom:3px; - letter-spacing:1.3px; - font-family:Arial, Helvetica, sans-serif; - font-size:0.9em; - color:#cdcdcd; - border-bottom:solid 1px #7e7e7e; - -} -.vb_wrap p { - font-family:Arial, Helvetica, sans-serif; - font-size:0.8em; - text-align:justify; - color:#e5e5e5; -} -.vb_wrap .text { - position:absolute; - background: url(../images/viewbox/ViewBox_bg.png) top left repeat; - border-top:solid 1px #121212; - bottom:0; left:0; - padding:5px; - width:99%; - z-index:2110; -} -* html .vb_wrap .text { - background-image:none; - background-color:#0f0e0e; -} -.vb_wrap .image { - z-index:2220; -} -.vb_wrap .text h1 { - margin:2px; - padding:1px; - color:#919191; - z-index:2230; -} -.vb_wrap .text .description { - position:relative; - bottom:0px; - left:0px; - padding:2px 2px; - font-family:Arial, Helvetica, sans-serif; - font-size:0.8em; - z-index:2260; - color:#949494; -} +/* Miniatury */ +.thumbdiv { + float:left; + position:relative; +} +.thumbdiv .title { + position:absolute; + padding:2px; + font-size:1.0em; + color:#fff; + background:#000; + border-top:solid 1px #000; + opacity:0.8; + bottom:14px; + left:8px; + right:9px; +} +/* VIEWBOX */ +#viewbox { + position:fixed; + height:100%; + width:100%; + top:0; + left:0; + background:#383739; + z-index:2190; + overflow:auto; +} +.vb_wrap { + position:absolute; + padding:10px; + height:20px; + width:20px; + background-image:url(../images/viewbox/ViewBox_bg.png); + background-color:#0f0e0e; + overflow:visible; + z-index:2200; + margin:0 auto; + top:0; + left:0; + border-radius:10px; + box-shadow: 4px 4px 7px #000; +} +.vb_wrap .content { + position:relative; + overflow:visible; +} +.vb_wrap .number { + position:absolute; + top:-32px; + left:1px; + font-size:0.9em; + color:#0c0c0c; +} +.vb_wrap .close { + position:absolute; + top:-40px; + right:-7px; + background:url(../images/viewbox/viewbox_close.png) top left no-repeat; + height:30px; + width:30px; +} +* html .vb_wrap .close { + top:-26px; + right:7px; +} +.vb_wrap .next { + position:absolute; + right:-60px; + background:url(../images/viewbox/ViewBox_next.png) top left no-repeat; + height:50px; + width:50px; + z-index:2190; +} +.vb_wrap .prev { + position:absolute; + left:-59px; + background:url(../images/viewbox/ViewBox_prev.png) top right no-repeat; + height:50px; + width:50px; + z-index:2190; +} +.vb_wrap .close a, .vb_wrap .next a, .vb_wrap .prev a { + display:block; + height:100%; + width:100%; + text-indent:-777em; + outline:none; +} +.vb_wrap h1 { + margin:5px 0px 8px; + padding-bottom:3px; + letter-spacing:1.3px; + font-family:Arial, Helvetica, sans-serif; + font-size:0.9em; + color:#cdcdcd; + border-bottom:solid 1px #7e7e7e; + +} +.vb_wrap p { + font-family:Arial, Helvetica, sans-serif; + font-size:0.8em; + text-align:justify; + color:#e5e5e5; +} +.vb_wrap .text { + position:absolute; + background: url(../images/viewbox/ViewBox_bg.png) top left repeat; + border-top:solid 1px #121212; + bottom:0; left:0; + padding:5px; + width:99%; + z-index:2110; +} +* html .vb_wrap .text { + background-image:none; + background-color:#0f0e0e; +} +.vb_wrap .image { + z-index:2220; +} +.vb_wrap .text h1 { + margin:2px; + padding:1px; + color:#919191; + z-index:2230; +} +.vb_wrap .text .description { + position:relative; + bottom:0px; + left:0px; + padding:2px 2px; + font-family:Arial, Helvetica, sans-serif; + font-size:0.8em; + z-index:2260; + color:#949494; +} diff --git a/forum/skins/default/templates/401.html b/forum/skins/default/templates/401.html index 7c1023f..0740e1f 100644 --- a/forum/skins/default/templates/401.html +++ b/forum/skins/default/templates/401.html @@ -2,7 +2,7 @@ {% load i18n %} {% block title %}{% trans "Not logged in" %}{% endblock %} {% block meta %} - + {% endblock %} {% block forestyle%} - -{% endblock %} - -{% block content %} - -
          -
          - - - - - -
          -
          - {% vote_buttons question request.user %} - {% favorite_mark question request.user %} -
          -
          -
          -
          - {{ question.html|safe }} -
          -
          - {% for tag in question.tagname_list %} - - {% endfor %} -
          -
          - {% post_controls question request.user %} - {% wiki_symbol request.user question %} -
          -
          - {% contributors_info question %} -
          - {% comments question request.user %} -
          - -
          - {% if question.nis.closed %} -
          -

          - {% blocktrans with question.nstate.closed.extra as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %} - {{ question.nstate.closed.by.username }} - {% diff_date question.nstate.closed.at %} -

          -
          - {% endif %} - {% if answers %} -
          -
          - -
          - {% blocktrans count answers.paginator.count as counter %}One Answer:{% plural %}{{counter}} Answers:{% endblocktrans %} -
          - {{ answers.paginator.sort_tabs }} -
          - {{ answers.paginator.page_numbers }} - - {% for answer in answers.paginator.page %} - -
          - - - - - -
          -
          - {% vote_buttons answer request.user %} - {% accept_button answer request.user %} -
          -
          -
          -
          - {{ answer.html|safe }} -
          -
          - {% post_controls answer request.user %} - {% wiki_symbol request.user answer %} -
          -
          - {% contributors_info answer %} -
          - {% comments answer request.user %} -
          -
          -
          - {% endfor %} -
          - {{ answers.paginator.page_numbers }} -
          - {% endif %} -
          - {% csrf_token %} -
          -
          - - {% if not question.closed %} -
          - {% spaceless %} -
          - {% if answers %} - {% trans "Your answer" %} - {% else %} - {% trans "Be the first one to answer this question!" %} - {% endif %} -
          - {% endspaceless %} -
          - {% comment %} - {% if not request.user.is_authenticated %} -
          {% trans "You can answer anonymously and then login." %}
          - {% else %} -

          - {% ifequal request.user question.author %} - {% trans "Answer your own question only to give an answer." %} - {% else %} - {% trans "Please only give an answer, no discussions." %} - {% endifequal %} - {% if not request.user.email_valid_and_can_answer %} - {% blocktrans %}Remember, your answer will not be published until you validate your email.{% endblocktrans %} - {% trans "Send me a validation link." %} - {% endif %} -

          - {% endif %} - {% endcomment %} - -
          -
          - {{ answer.text }} -
          - - - - - {% if settings.WIKI_ON %} - - {% endif %} - - -
          - - {% trans "toggle preview" %} - - - {{ answer.wiki }} - - {{ answer.wiki.label_tag }} - -
          -
          - {{ answer.text.errors }} -
          -
          - - {% if answer.recaptcha %} -
          - {{ answer.recaptcha.errors }} - {{ answer.recaptcha }} -
          -
          - {% endif %} - -

          - - {% endif %} -
          -
          -
          -{% endblock %} - -{% block sidebar %} -
          - {% include "subscription_status.html" %} -
          - -{% markdown_help %} - -{% sidebar_upper %} - -{% cache 60 questions_tags settings.APP_URL question.id %} -
          -

          - {% trans "Question tags" %}: -

          -

          - {% for tag in question.tags.all %} - ×{{ tag.used_count|intcomma }}
          - {% endfor %} -

          -

          - {% trans "question asked" %}: {% diff_date question.added_at %} -

          -

          - {% trans "question was seen" %}: {{ question.view_count|intcomma }} {% trans "times" %} -

          -

          - {% trans "last updated" %}: {% diff_date question.last_activity_at %} -

          -
          -{% endcache %} -{% sidebar_lower %} -
          -

          {% trans "Related questions" %}

          - -
          - -{% endblock %} - -{% block endjs %} -{% endblock %} - +{% extends "base.html" %} + +{% load node_tags %} +{% load extra_tags %} +{% load extra_filters %} +{% load general_sidebar_tags %} +{% load smart_if %} +{% load humanize %} +{% load i18n %} +{% load cache %} +{% block metadescription %}{{ question.meta_description }}{% endblock %} +{% block metakeywords %}{{question.tagname_meta_generator}}{% endblock %} +{% block meta %} + + +{% endblock %} +{% block title %}{% spaceless %}{{ question.headline }}{% endspaceless %}{% endblock %} +{% block forejs %} + {% if not question.nis.closed %} + + + + + + + + {% if embed_youtube_videos %} + + + + {% endif %} + {% endif %} + + + +{% endblock %} + +{% block content %} + +
          +
          + + + + + +
          +
          + {% vote_buttons question request.user %} + {% favorite_mark question request.user %} +
          +
          +
          +
          + {{ question.html|safe }} +
          +
          + {% for tag in question.tagname_list %} + + {% endfor %} +
          +
          + {% post_controls question request.user %} + {% wiki_symbol request.user question %} +
          +
          + {% contributors_info question %} +
          + {% comments question request.user %} +
          + +
          + {% if question.nis.closed %} +
          +

          + {% blocktrans with question.nstate.closed.extra as close_reason %}The question has been closed for the following reason "{{ close_reason }}" by{% endblocktrans %} + {{ question.nstate.closed.by.username }} + {% diff_date question.nstate.closed.at %} +

          +
          + {% endif %} + {% if answers %} +
          +
          + +
          + {% blocktrans count answers.paginator.count as counter %}One Answer:{% plural %}{{counter}} Answers:{% endblocktrans %} +
          + {{ answers.paginator.sort_tabs }} +
          + {{ answers.paginator.page_numbers }} + + {% for answer in answers.paginator.page %} + +
          + + + + + +
          +
          + {% vote_buttons answer request.user %} + {% accept_button answer request.user %} +
          +
          +
          +
          + {{ answer.html|safe }} +
          +
          + {% post_controls answer request.user %} + {% wiki_symbol request.user answer %} +
          +
          + {% contributors_info answer %} +
          + {% comments answer request.user %} +
          +
          +
          + {% endfor %} +
          + {{ answers.paginator.page_numbers }} +
          + {% endif %} +
          + {% csrf_token %} +
          +
          + + {% if not question.closed %} +
          + {% spaceless %} +
          + {% if answers %} + {% trans "Your answer" %} + {% else %} + {% trans "Be the first one to answer this question!" %} + {% endif %} +
          + {% endspaceless %} +
          + {% comment %} + {% if not request.user.is_authenticated %} +
          {% trans "You can answer anonymously and then login." %}
          + {% else %} +

          + {% ifequal request.user question.author %} + {% trans "Answer your own question only to give an answer." %} + {% else %} + {% trans "Please only give an answer, no discussions." %} + {% endifequal %} + {% if not request.user.email_valid_and_can_answer %} + {% blocktrans %}Remember, your answer will not be published until you validate your email.{% endblocktrans %} + {% trans "Send me a validation link." %} + {% endif %} +

          + {% endif %} + {% endcomment %} + +
          +
          + {{ answer.text }} +
          + + + + + {% if settings.WIKI_ON %} + + {% endif %} + + +
          + + {% trans "toggle preview" %} + + + {{ answer.wiki }} + + {{ answer.wiki.label_tag }} + +
          +
          + {{ answer.text.errors }} +
          +
          + + {% if answer.recaptcha %} +
          + {{ answer.recaptcha.errors }} + {{ answer.recaptcha }} +
          +
          + {% endif %} + +

          + + {% endif %} +
          +
          +
          +{% endblock %} + +{% block sidebar %} +
          + {% include "subscription_status.html" %} +
          + +{% markdown_help %} + +{% sidebar_upper %} + +{% cache 60 questions_tags settings.APP_URL question.id %} +
          +

          + {% trans "Question tags" %}: +

          +

          + {% for tag in question.tags.all %} + ×{{ tag.used_count|intcomma }}
          + {% endfor %} +

          +

          + {% trans "question asked" %}: {% diff_date question.added_at %} +

          +

          + {% trans "question was seen" %}: {{ question.view_count|intcomma }} {% trans "times" %} +

          +

          + {% trans "last updated" %}: {% diff_date question.last_activity_at %} +

          +
          +{% endcache %} +{% sidebar_lower %} +
          +

          {% trans "Related questions" %}

          + +
          + +{% endblock %} + +{% block endjs %} +{% endblock %} + diff --git a/forum/skins/default/templates/question_edit.html b/forum/skins/default/templates/question_edit.html index 4f43950..b3fcd94 100644 --- a/forum/skins/default/templates/question_edit.html +++ b/forum/skins/default/templates/question_edit.html @@ -27,7 +27,7 @@ }); //Tags autocomplete action - $("#id_tags").autocomplete("{% url matching_tags %}", { + $("#id_tags").autocomplete("{% url "matching_tags" %}", { matchContains: true, max: 20, multiple: true, diff --git a/forum/skins/default/templates/question_edit_tips.html b/forum/skins/default/templates/question_edit_tips.html index ce7ac95..ea12bdd 100644 --- a/forum/skins/default/templates/question_edit_tips.html +++ b/forum/skins/default/templates/question_edit_tips.html @@ -1,12 +1,11 @@ -{% load markup %} {% load i18n general_sidebar_tags %}

          {% trans "Title Tips" %}

          {{ settings.QUESTION_TITLE_TIPS|markdown:"settingsparser" }}
          @@ -22,4 +21,4 @@ - \ No newline at end of file + diff --git a/forum/skins/default/templates/question_list/count.html b/forum/skins/default/templates/question_list/count.html index dbb45eb..1e806dd 100644 --- a/forum/skins/default/templates/question_list/count.html +++ b/forum/skins/default/templates/question_list/count.html @@ -1,26 +1,26 @@ -{% spaceless %} -{% load i18n humanize extra_tags %} -{% declare %} - answer_count = questions.children_count('answer') -{% enddeclare %} - -
          -
          - {{ questions.paginator.count }}{{ list_description }} -
          - - - {% if answer_count %} -
          - {{ answer_count }}{% trans "answers" %} -
          - - {% endif %} - -
          -

          - {{ questions.paginator.sort_description }} -

          -
          -
          +{% spaceless %} +{% load i18n humanize extra_tags %} +{% declare %} + answer_count = questions.children_count('answer') +{% enddeclare %} + +
          +
          + {{ questions.paginator.count }}{{ list_description }} +
          + + + {% if answer_count %} +
          + {{ answer_count }}{% trans "answers" %} +
          + + {% endif %} + +
          +

          + {{ questions.paginator.sort_description }} +

          +
          +
          {% endspaceless %} \ No newline at end of file diff --git a/forum/skins/default/templates/question_list/item.html b/forum/skins/default/templates/question_list/item.html index 1ad2ba5..e3dd4b3 100644 --- a/forum/skins/default/templates/question_list/item.html +++ b/forum/skins/default/templates/question_list/item.html @@ -1,40 +1,40 @@ -{% load i18n humanize extra_filters extra_tags user_tags %} -
          -
          {% if favorite_count %} -
          - -
          {{question.favourite_count|intcomma}}
          -
          - {% endif %} -
          -
          {{question.score|intcomma}}
          -
          {% ifequal question.score 1 %}{% trans "vote" %}{% else %}{% trans "votes" %}{% endifequal %}
          -
          -
          -
          {{question.answer_count|intcomma}}
          -
          {% ifequal question.answer_count 1 %}{% trans "answer" %}{% else %}{% trans "answers" %}{% endifequal %}
          -
          -
          -
          {{question.view_count|decorated_int|safe}}
          -
          {% ifequal question.view_count 1 %}{% trans "view" %}{% else %}{% trans "views" %}{% endifequal %}
          -
          -
          - -
          -

          {{question.headline}}

          - {% if question_summary %} -
          - {{ question.summary }} -
          - {% endif %} -
          - {% diff_date question.last_activity_at %} - {% if question.last_activity_by %}{% user_signature question.last_activity_by signature_type %}{% endif %} -
          - -
          {% for tag in question.tagname_list %} - {% endfor %} -
          -
          - +{% load i18n humanize extra_filters extra_tags user_tags %} +
          +
          {% if favorite_count %} +
          + +
          {{question.favourite_count|intcomma}}
          +
          + {% endif %} +
          +
          {{question.score|intcomma}}
          +
          {% ifequal question.score 1 %}{% trans "vote" %}{% else %}{% trans "votes" %}{% endifequal %}
          +
          +
          +
          {{question.answer_count|intcomma}}
          +
          {% ifequal question.answer_count 1 %}{% trans "answer" %}{% else %}{% trans "answers" %}{% endifequal %}
          +
          +
          +
          {{question.view_count|decorated_int|safe}}
          +
          {% ifequal question.view_count 1 %}{% trans "view" %}{% else %}{% trans "views" %}{% endifequal %}
          +
          +
          + +
          +

          {{question.headline}}

          + {% if question_summary %} +
          + {{ question.summary }} +
          + {% endif %} +
          + {% diff_date question.last_activity_at %} + {% if question.last_activity_by %}{% user_signature question.last_activity_by signature_type %}{% endif %} +
          + +
          {% for tag in question.tagname_list %} + {% endfor %} +
          +
          +
          \ No newline at end of file diff --git a/forum/skins/default/templates/question_list/related_tags.html b/forum/skins/default/templates/question_list/related_tags.html index 6ec2a75..8a2bae5 100644 --- a/forum/skins/default/templates/question_list/related_tags.html +++ b/forum/skins/default/templates/question_list/related_tags.html @@ -1,15 +1,15 @@ -{% load i18n %} -{% load humanize %} - -{% if tags %} -
          -

          {% trans "Related tags" %}

          -
          - {% for tag in tags %} - - × {{ tag.used_count|intcomma }} -
          - {% endfor %} -
          -
          +{% load i18n %} +{% load humanize %} + +{% if tags %} +
          +

          {% trans "Related tags" %}

          +
          + {% for tag in tags %} + + × {{ tag.used_count|intcomma }} +
          + {% endfor %} +
          +
          {% endif %} \ No newline at end of file diff --git a/forum/skins/default/templates/question_list/sort_tabs.html b/forum/skins/default/templates/question_list/sort_tabs.html index eff0408..5e35fea 100644 --- a/forum/skins/default/templates/question_list/sort_tabs.html +++ b/forum/skins/default/templates/question_list/sort_tabs.html @@ -1,8 +1,8 @@ -{% load i18n %} - - +{% load i18n %} + + diff --git a/forum/skins/default/templates/question_list/subscription_item.html b/forum/skins/default/templates/question_list/subscription_item.html index 3f1f688..faa0e18 100644 --- a/forum/skins/default/templates/question_list/subscription_item.html +++ b/forum/skins/default/templates/question_list/subscription_item.html @@ -1,6 +1,6 @@ {% load i18n humanize extra_filters extra_tags user_tags %}
          -
          + diff --git a/forum/skins/default/templates/question_list/tag_selector.html b/forum/skins/default/templates/question_list/tag_selector.html index 89817a9..ab87ba5 100644 --- a/forum/skins/default/templates/question_list/tag_selector.html +++ b/forum/skins/default/templates/question_list/tag_selector.html @@ -12,7 +12,7 @@ + href="{% url "tag_questions" tag_name|urlencode %}">{{tag_name}} @@ -30,7 +30,7 @@ + href="{% url "tag_questions" tag_name|urlencode %}">{{tag_name}} diff --git a/forum/skins/default/templates/question_list/title.html b/forum/skins/default/templates/question_list/title.html index bb6268f..dcec33d 100644 --- a/forum/skins/default/templates/question_list/title.html +++ b/forum/skins/default/templates/question_list/title.html @@ -1,25 +1,25 @@ -{% load i18n %} - -
          - {% if searchtag %} - {% trans "Found by tags" %} - {% else %} - {% if searchtitle %} - {% if settings.USE_SPHINX_SEARCH %} - {% trans "Search results" %} - {% else %} - {% trans "Found by title" %} - {% endif %} - {% else %} - {% if is_unanswered %} - {% trans "Unanswered questions" %} - {% else %} - {% if page_title %} - {% trans page_title %} - {% else %} - {% trans "All Questions" %} - {% endif %} - {% endif %} - {% endif %} - {% endif %} +{% load i18n %} + +
          + {% if searchtag %} + {% trans "Found by tags" %} + {% else %} + {% if searchtitle %} + {% if settings.USE_SPHINX_SEARCH %} + {% trans "Search results" %} + {% else %} + {% trans "Found by title" %} + {% endif %} + {% else %} + {% if is_unanswered %} + {% trans "Unanswered questions" %} + {% else %} + {% if page_title %} + {% trans page_title %} + {% else %} + {% trans "All Questions" %} + {% endif %} + {% endif %} + {% endif %} + {% endif %}
          \ No newline at end of file diff --git a/forum/skins/default/templates/question_retag.html b/forum/skins/default/templates/question_retag.html index 6a5266c..dcb8058 100644 --- a/forum/skins/default/templates/question_retag.html +++ b/forum/skins/default/templates/question_retag.html @@ -7,7 +7,7 @@ - - - - - - {% block userjs %} - {% endblock %} -{% endblock %} -{% block content %} -
          - {{ user.username }} - {% trans "edit profile" %} -
          -
          -
          - {% csrf_token %} -
          - {% if user.email %} - {% gravatar user 128 %} - {% else %} - - {% endif %} - -
          - -
          -

          {% trans "Registered user" %}

          - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          {% trans "Screen Name" %}: - {% if form.username %} - {{ form.username }} {{ form.username.errors }} - {% else %} - {{ user.username }} - {% endif %} -
          {{ form.email.label_tag }}:{{ form.email }} {{ form.email.errors }}
          {{ form.email.help_text }}
          {{ form.realname.label_tag }}:{{ form.realname }} {{ form.realname.errors }}
          {{ form.website.label_tag }}:{{ form.website }} {{ form.website.errors }}
          {{ form.city.label_tag }}:{{ form.city }} {{ form.city.errors }}
          {{ form.birthday.label_tag }}:{{ form.birthday }} {{ form.birthday.errors }}
          {{ form.about.label_tag }}:{{ form.about }} {{ form.about.errors }}
          -
          - - - -
          -
          -
          - -
          -{% endblock %} - +{% extends "base_content.html" %} + +{% load extra_tags %} +{% load humanize %} +{% load i18n %} +{% block title %}{% spaceless %}{% trans "Edit user profile" %}{% endspaceless %}{% endblock %} +{% block forejs %} + + + + + + + {% block userjs %} + {% endblock %} +{% endblock %} +{% block content %} +
          + {{ user.username }} - {% trans "edit profile" %} +
          +
          +
          + {% csrf_token %} +
          + {% if user.email %} + {% gravatar user 128 %} + {% else %} + + {% endif %} + +
          + +
          +

          {% trans "Registered user" %}

          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          {% trans "Screen Name" %}: + {% if form.username %} + {{ form.username }} {{ form.username.errors }} + {% else %} + {{ user.username }} + {% endif %} +
          {{ form.email.label_tag }}:{{ form.email }} {{ form.email.errors }}
          {{ form.email.help_text }}
          {{ form.realname.label_tag }}:{{ form.realname }} {{ form.realname.errors }}
          {{ form.website.label_tag }}:{{ form.website }} {{ form.website.errors }}
          {{ form.city.label_tag }}:{{ form.city }} {{ form.city.errors }}
          {{ form.birthday.label_tag }}:{{ form.birthday }} {{ form.birthday.errors }}
          {{ form.about.label_tag }}:{{ form.about }} {{ form.about.errors }}
          +
          + + + +
          +
          +
          + +
          +{% endblock %} + diff --git a/forum/skins/default/templates/users/info.html b/forum/skins/default/templates/users/info.html index a99691c..7937ea0 100644 --- a/forum/skins/default/templates/users/info.html +++ b/forum/skins/default/templates/users/info.html @@ -4,7 +4,6 @@ {% load humanize %} {% load smart_if %} {% load i18n %} -{% load markup %} {% load user_tags %}
          @@ -95,7 +94,7 @@ {% if not view_user.email_isvalid %} ({% trans "not validated" %}) {% ifequal request.user view_user %} - {% trans "Send me a validation link." %} + {% trans "Send me a validation link." %} {% endifequal %} {% endif %} @@ -104,7 +103,7 @@ - + Report user diff --git a/forum/skins/default/templates/users/menu.html b/forum/skins/default/templates/users/menu.html index 24d213d..1a9e57b 100644 --- a/forum/skins/default/templates/users/menu.html +++ b/forum/skins/default/templates/users/menu.html @@ -1,8 +1,8 @@ -{% load i18n smart_if ui_registry %} - -
          - {% trans "User tools" %} ▼ -
            - {% loadregistry user_menu %}{% endloadregistry %} -
          -
          +{% load i18n smart_if ui_registry %} + +
          + {% trans "User tools" %} ▼ +
            + {% loadregistry user_menu %}{% endloadregistry %} +
          +
          diff --git a/forum/skins/default/templates/users/signature.html b/forum/skins/default/templates/users/signature.html index e8aebf3..dbea26c 100644 --- a/forum/skins/default/templates/users/signature.html +++ b/forum/skins/default/templates/users/signature.html @@ -1,33 +1,33 @@ -{% load i18n extra_filters %}{% spaceless %} - -{% if not user.is_suspended %} - {% ifequal format "full" %} - - {% else %} - {{ user.decorated_name }} - {{ user.reputation|decorated_int:"" }} - {% ifequal format "badges" %} - {% if user.gold %} - - - {{ user.gold }} - - {% endif %} - {% if user.silver %} - - - {{ user.silver }} - - {% endif %} - {% if user.bronze %} - - - {{ user.bronze }} - - {% endif %} - {% endifequal %} - {% endifequal %} -{% else %} - {{ user.decorated_name }}{% trans "(suspended)" %} -{% endif %} -{% endspaceless %} +{% load i18n extra_filters %}{% spaceless %} + +{% if not user.is_suspended %} + {% ifequal format "full" %} + + {% else %} + {{ user.decorated_name }} + {{ user.reputation|decorated_int:"" }} + {% ifequal format "badges" %} + {% if user.gold %} + + + {{ user.gold }} + + {% endif %} + {% if user.silver %} + + + {{ user.silver }} + + {% endif %} + {% if user.bronze %} + + + {{ user.bronze }} + + {% endif %} + {% endifequal %} + {% endifequal %} +{% else %} + {{ user.decorated_name }}{% trans "(suspended)" %} +{% endif %} +{% endspaceless %} diff --git a/forum/skins/default/templates/users/stats.html b/forum/skins/default/templates/users/stats.html index 358771d..bcc2f52 100644 --- a/forum/skins/default/templates/users/stats.html +++ b/forum/skins/default/templates/users/stats.html @@ -110,7 +110,7 @@ + href="{% url "tag_questions" tag|urlencode %}?user={{view_user.username}}">{{tag.name}} × {{ tag.user_tag_usage_count|intcomma }}
          {% if forloop.counter|divisibleby:"10" %} @@ -137,7 +137,7 @@ {% for award, count in awards %} {% spaceless %} - +  {{ award.name }} {% ifnotequal count 1 %} diff --git a/forum/skins/default/templates/users/subscriptions_management.html b/forum/skins/default/templates/users/subscriptions_management.html index a0d921e..54a68ac 100644 --- a/forum/skins/default/templates/users/subscriptions_management.html +++ b/forum/skins/default/templates/users/subscriptions_management.html @@ -6,9 +6,9 @@ {% trans "Manage your current subscriptions" %} {% if auto %} - {% trans "don't show auto-subscribe" %} + {% trans "don't show auto-subscribe" %} {% else %} - {% trans "show auto-subscribe" %} + {% trans "show auto-subscribe" %} {% endif %} diff --git a/forum/skins/default/templates/users/subscriptions_settings.html b/forum/skins/default/templates/users/subscriptions_settings.html index 7675db3..dc9db08 100644 --- a/forum/skins/default/templates/users/subscriptions_settings.html +++ b/forum/skins/default/templates/users/subscriptions_settings.html @@ -1,108 +1,108 @@ -{% load i18n %} -{% load extra_tags %} -{% load humanize %} - -

          {% trans "Notifications and subscription settings" %}

          -

          - {% blocktrans %} - Here you can decide which types of notifications you wish to receive, and their frequency.
          - {% endblocktrans %} -

          -
          -
          - {% csrf_token %} - {{ form.errors }} - - - - - - - - - - - - - - - - - - - - -
          - {% trans "Notify me when:" %} -
          {% trans "A new member joins" %}{{ form.member_joins }}
          {% trans "A new question is posted" %}{{ form.new_question }}
          {% trans "A new question matching my interesting tags is posted" %}{{ form.new_question_watched_tags }}
          {% trans "There's an update on one of my subscriptions" %}{{ form.subscribed_questions }}
          -

           

          - - - - - - - - - -
          - {% trans "Auto subscribe me to:" %} -
          - {{ form.questions_viewed }}{% trans "Questions I view" %} - - {{ form.all_questions_watched_tags }}{% trans "All questions matching my interesting tags" %} - - {{ form.all_questions }}{% trans "All questions" %} -
          -

           

          - - - - - - - - - - - - -
          - {% trans "On my subscriptions, notify me when:" %} -
          - {{ form.notify_answers }}{% trans "An answer is posted" %} - - {{ form.notify_comments_own_post }}{% trans "A comment on one of my posts is posted" %} -
          - {{ form.notify_comments }}{% trans "A comment is posted" %} - - {{ form.notify_accepted }}{% trans "An answer is accepted" %} -
          -

           

          - - - - - - - - - - - - - -
          - {% trans "Daily Digest:" %} -
          - {{ form.send_digest }}{% trans "Send me the daily digest with information about the site activity" %} -
          - {% trans "Notify When I'm Discussed:" %} -
          - {{ form.notify_reply_to_comments }}{% trans "Notify me when someone replies to one of my comments on any post using the
          @username
          notation" %} -
          -
          - -
          -
          -
          +{% load i18n %} +{% load extra_tags %} +{% load humanize %} + +

          {% trans "Notifications and subscription settings" %}

          +

          + {% blocktrans %} + Here you can decide which types of notifications you wish to receive, and their frequency.
          + {% endblocktrans %} +

          +
          +
          + {% csrf_token %} + {{ form.errors }} + + + + + + + + + + + + + + + + + + + + +
          + {% trans "Notify me when:" %} +
          {% trans "A new member joins" %}{{ form.member_joins }}
          {% trans "A new question is posted" %}{{ form.new_question }}
          {% trans "A new question matching my interesting tags is posted" %}{{ form.new_question_watched_tags }}
          {% trans "There's an update on one of my subscriptions" %}{{ form.subscribed_questions }}
          +

           

          + + + + + + + + + +
          + {% trans "Auto subscribe me to:" %} +
          + {{ form.questions_viewed }}{% trans "Questions I view" %} + + {{ form.all_questions_watched_tags }}{% trans "All questions matching my interesting tags" %} + + {{ form.all_questions }}{% trans "All questions" %} +
          +

           

          + + + + + + + + + + + + +
          + {% trans "On my subscriptions, notify me when:" %} +
          + {{ form.notify_answers }}{% trans "An answer is posted" %} + + {{ form.notify_comments_own_post }}{% trans "A comment on one of my posts is posted" %} +
          + {{ form.notify_comments }}{% trans "A comment is posted" %} + + {{ form.notify_accepted }}{% trans "An answer is accepted" %} +
          +

           

          + + + + + + + + + + + + + +
          + {% trans "Daily Digest:" %} +
          + {{ form.send_digest }}{% trans "Send me the daily digest with information about the site activity" %} +
          + {% trans "Notify When I'm Discussed:" %} +
          + {{ form.notify_reply_to_comments }}{% trans "Notify me when someone replies to one of my comments on any post using the
          @username
          notation" %} +
          +
          + +
          +
          +
          diff --git a/forum/templatetags/__init__.py b/forum/templatetags/__init__.py index 56a0b8b..de87a64 100644 --- a/forum/templatetags/__init__.py +++ b/forum/templatetags/__init__.py @@ -1,7 +1,7 @@ -import re - -splitter = re.compile(r'\s*=\s*') -matcher = re.compile(r'^.+=.+$') - -def argument_parser(arguments): +import re + +splitter = re.compile(r'\s*=\s*') +matcher = re.compile(r'^.+=.+$') + +def argument_parser(arguments): return dict(splitter.split(s) for s in arguments if matcher.match(s)) \ No newline at end of file diff --git a/forum/templatetags/extra_tags.py b/forum/templatetags/extra_tags.py index 1acb969..0226007 100644 --- a/forum/templatetags/extra_tags.py +++ b/forum/templatetags/extra_tags.py @@ -13,7 +13,6 @@ from django.utils import dateformat from forum.models import Question, Answer, QuestionRevision, AnswerRevision, NodeRevision from django.utils.translation import ugettext as _ from django.utils.translation import ungettext -from django.utils import simplejson from forum import settings from django.template.defaulttags import url as default_url from forum import skins diff --git a/forum/templatetags/general_sidebar_tags.py b/forum/templatetags/general_sidebar_tags.py index 2f70fa2..ea83099 100644 --- a/forum/templatetags/general_sidebar_tags.py +++ b/forum/templatetags/general_sidebar_tags.py @@ -1,39 +1,39 @@ -from django import template -from forum.models import Tag, Award -from forum import settings - -from extra_filters import static_content - -register = template.Library() - -@register.inclusion_tag('sidebar/markdown_help.html') -def markdown_help(): - return {} - -@register.inclusion_tag('sidebar/recent_awards.html') -def recent_awards(): - return {'awards': Award.objects.order_by('-awarded_at')[:settings.RECENT_AWARD_SIZE]} - -@register.inclusion_tag('sidebar/user_blocks.html') -def sidebar_upper(): - return { - 'show': settings.SIDEBAR_UPPER_SHOW, - 'content': static_content(settings.SIDEBAR_UPPER_TEXT, settings.SIDEBAR_UPPER_RENDER_MODE), - 'wrap': not settings.SIDEBAR_UPPER_DONT_WRAP, - 'blockid': 'sidebar-upper' - } - -@register.inclusion_tag('sidebar/user_blocks.html') -def sidebar_lower(): - return { - 'show': settings.SIDEBAR_LOWER_SHOW, - 'content': static_content(settings.SIDEBAR_LOWER_TEXT, settings.SIDEBAR_LOWER_RENDER_MODE), - 'wrap': not settings.SIDEBAR_LOWER_DONT_WRAP, - 'blockid': 'sidebar-lower' - } - -@register.inclusion_tag('sidebar/recent_tags.html') -def recent_tags(): - return {'tags': Tag.active.order_by('-id')[:settings.RECENT_TAGS_SIZE]} - +from django import template +from forum.models import Tag, Award +from forum import settings + +from extra_filters import static_content + +register = template.Library() + +@register.inclusion_tag('sidebar/markdown_help.html') +def markdown_help(): + return {} + +@register.inclusion_tag('sidebar/recent_awards.html') +def recent_awards(): + return {'awards': Award.objects.order_by('-awarded_at')[:settings.RECENT_AWARD_SIZE]} + +@register.inclusion_tag('sidebar/user_blocks.html') +def sidebar_upper(): + return { + 'show': settings.SIDEBAR_UPPER_SHOW, + 'content': static_content(settings.SIDEBAR_UPPER_TEXT, settings.SIDEBAR_UPPER_RENDER_MODE), + 'wrap': not settings.SIDEBAR_UPPER_DONT_WRAP, + 'blockid': 'sidebar-upper' + } + +@register.inclusion_tag('sidebar/user_blocks.html') +def sidebar_lower(): + return { + 'show': settings.SIDEBAR_LOWER_SHOW, + 'content': static_content(settings.SIDEBAR_LOWER_TEXT, settings.SIDEBAR_LOWER_RENDER_MODE), + 'wrap': not settings.SIDEBAR_LOWER_DONT_WRAP, + 'blockid': 'sidebar-lower' + } + +@register.inclusion_tag('sidebar/recent_tags.html') +def recent_tags(): + return {'tags': Tag.active.order_by('-id')[:settings.RECENT_TAGS_SIZE]} + \ No newline at end of file diff --git a/forum/templatetags/node_tags.py b/forum/templatetags/node_tags.py index be54232..ff63ec8 100644 --- a/forum/templatetags/node_tags.py +++ b/forum/templatetags/node_tags.py @@ -1,267 +1,267 @@ -from datetime import datetime, timedelta -import re - -from forum.models import Question, Action -from django.template import Template, Context -from django.utils.translation import ungettext, ugettext as _ -from django.utils.html import strip_tags -from django.utils.encoding import smart_unicode -from django.utils.safestring import mark_safe -from django.conf import settings as django_settings -from django.core.urlresolvers import reverse -from django import template -from forum.actions import * -from forum import settings - -register = template.Library() - -@register.inclusion_tag('node/vote_buttons.html') -def vote_buttons(post, user): - context = dict(post=post, user_vote='none') - - if user.is_authenticated(): - context['user_vote'] = {1: 'up', -1: 'down', None: 'none'}[VoteAction.get_for(user, post)] - - return context - -@register.inclusion_tag('node/accept_button.html') -def accept_button(answer, user): - if not settings.DISABLE_ACCEPTING_FEATURE: - return { - 'can_accept': user.is_authenticated() and user.can_accept_answer(answer), - 'answer': answer, - 'user': user - } - else: - return '' - -@register.inclusion_tag('node/wiki_symbol.html') -def wiki_symbol(user, post): - context = { - 'is_wiki': post.nis.wiki, - 'post_type': post.friendly_name - } - - if post.nis.wiki: - if user.can_edit_post(post): - context['can_edit'] = True - context['edit_url'] = reverse('edit_' + post.node_type, kwargs={'id': post.id}) - context['by'] = post.nstate.wiki.by.username - context['at'] = post.nstate.wiki.at - - return context - -@register.inclusion_tag('node/favorite_mark.html') -def favorite_mark(question, user): - try: - FavoriteAction.objects.get(canceled=False, node=question, user=user) - favorited = True - except: - favorited = False - - return {'favorited': favorited, 'favorite_count': question.favorite_count, 'question': question} - -@register.simple_tag -def post_classes(post): - classes = [] - - if post.nis.deleted: - classes.append('deleted') - - if post.node_type == "answer": - if (not settings.DISABLE_ACCEPTING_FEATURE) and post.nis.accepted: - classes.append('accepted-answer') - - if post.author == post.question.author: - classes.append('answered-by-owner') - - return " ".join(classes) - -def post_control(text, url, command=False, withprompt=False, confirm=False, title="", copy=False, extra_classes=[]): - classes = (command and "ajax-command" or " ") + (withprompt and " withprompt" or " ") + (confirm and " confirm" or " ") + \ - (copy and " copy" or " ") - - for extra_class in extra_classes: - classes += " %s" % extra_class - - return {'text': text, 'url': url, 'classes': classes, 'title': title} - - -moderation_enabled = False -for m in django_settings.MODULE_LIST: - if m.__name__.endswith('moderation'): - moderation_enabled = True - -@register.inclusion_tag('node/post_controls.html' if not moderation_enabled else "modules/moderation/node/post_controls.html") -def post_controls(post, user): - controls = [] - menu = [] - post_type = post.node_type - - # We show the link tool if the post is an Answer. It is visible to Guests too. - if post_type == "answer": - # Answer permanent link tool - controls.append(post_control(_('permanent link'), reverse('answer_permanent_link', kwargs={'id' : post.id,}), - title=_("answer permanent link"), command=True, withprompt=True, copy=True)) - - # Users should be able to award points for an answer. Users cannot award their own answers - if user != post.author and user.is_authenticated() and user.reputation > 1: - controls.append(post_control(_("award points"), reverse('award_points', kwargs={'user_id' : post.author.id, - 'answer_id' : post.id}), title=_("award points to %s") % smart_unicode(post.author.username), - command=True, withprompt=True)) - - # The other controls are visible only to authenticated users. - if user.is_authenticated(): - try: - edit_url = reverse('edit_' + post_type, kwargs={'id': post.id}) - if user.can_edit_post(post): - controls.append(post_control(_('edit'), edit_url)) - elif post_type == 'question' and user.can_retag_questions(): - controls.append(post_control(_('retag'), edit_url)) - except: - pass - - if post_type == 'question': - if post.nis.closed and user.can_reopen_question(post): - controls.append(post_control(_('reopen'), reverse('reopen', kwargs={'id': post.id}), command=True)) - elif not post.nis.closed and user.can_close_question(post): - controls.append(post_control(_('close'), reverse('close', kwargs={'id': post.id}), command=True, withprompt=True)) - - if user.can_flag_offensive(post): - label = _('report') - - if user.can_view_offensive_flags(post): - label = "%s (%d)" % (label, post.flag_count) - - - report_control = post_control(label, reverse('flag_post', kwargs={'id': post.id}), - command=True, withprompt=True, - title=_("report as offensive (i.e containing spam, advertising, malicious text, etc.)")) - - # Depending on the setting choose where to attach the control - if settings.REPORT_OFFENSIVE_CONTROL_POSITION.value == "more": - menu.append(report_control) - else: - controls.append(report_control) - - if user.can_delete_post(post): - if post.nis.deleted: - controls.append(post_control(_('undelete'), reverse('delete_post', kwargs={'id': post.id}), - command=True, confirm=True)) - else: - controls.append(post_control(_('delete'), reverse('delete_post', kwargs={'id': post.id}), - command=True, confirm=True)) - - if user.can_delete_post(post): - menu.append(post_control(_('see revisions'), - reverse('revisions', - kwargs={'id': post.id}), - command=False, confirm=False)) - - if settings.WIKI_ON: - if (not post.nis.wiki) and user.can_wikify(post): - menu.append(post_control(_('mark as community wiki'), reverse('wikify', kwargs={'id': post.id}), - command=True, confirm=True)) - - elif post.nis.wiki and user.can_cancel_wiki(post): - menu.append(post_control(_('cancel community wiki'), reverse('wikify', kwargs={'id': post.id}), - command=True, confirm=True)) - - if post.node_type == "answer" and user.can_convert_to_comment(post): - menu.append(post_control(_('convert to comment'), reverse('convert_to_comment', kwargs={'id': post.id}), - command=True, withprompt=True)) - - if post.node_type == "answer" and user.can_convert_to_question(post): - menu.append(post_control(_('convert to question'), reverse('convert_to_question', kwargs={'id': post.id}), - command=False, confirm=True)) - - if user.is_superuser or user.is_staff: - plain_text = strip_tags(post.html) - - char_count = len(plain_text) - fullStr = plain_text + " " - left_trimmedStr = re.sub(re.compile(r"^[^\w]+", re.IGNORECASE), "", fullStr) - cleanedStr = re.sub(re.compile(r"[^\w]+", re.IGNORECASE), " ", left_trimmedStr) - splitString = cleanedStr.split(" ") - word_count = len(splitString) - 1 - - metrics = mark_safe("%s %s / %s %s" % (char_count, ungettext('character', 'characters', char_count), - word_count, ungettext('word', 'words', word_count))) - - menu.append(post_control(metrics, "#", command=False, withprompt=False)) - - return {'controls': controls, 'menu': menu, 'post': post, 'user': user} - -def _comments(post, user): - all_comments = post.comments.filter_state(deleted=False)\ - .order_by('-added_at' if settings.SHOW_LATEST_COMMENTS_FIRST else 'added_at') - - if len(all_comments) <= 5: - top_scorers = all_comments - else: - top_scorers = sorted(all_comments, lambda c1, c2: cmp(c2.score, c1.score))[0:5] - - comments = [] - showing = 0 - for c in all_comments: - context = { - 'can_delete': user.can_delete_comment(c), - 'can_like': user.can_like_comment(c), - 'can_edit': user.can_edit_comment(c), - 'can_convert': user.can_convert_comment_to_answer(c) - } - - if c in top_scorers or c.is_reply_to(user): - context['top_scorer'] = True - showing += 1 - - if context['can_like']: - context['likes'] = VoteAction.get_for(user, c) == 1 - - context['user'] = c.user - context['comment'] = c.comment - context.update(dict(c.__dict__)) - comments.append(context) - - # Generate canned comments - canned_comments = [] - for comment in settings.CANNED_COMMENTS: - t = Template(smart_unicode(comment)) - c = Context({ - 'post' : post, - 'settings' : settings, - }) - canned_comments.append(t.render(c)) - - total = len(all_comments) - return { - 'comments': comments, - 'canned_comments': canned_comments, - 'post': post, - 'can_comment': user.can_comment(post), - 'max_length': settings.FORM_MAX_COMMENT_BODY, - 'min_length': settings.FORM_MIN_COMMENT_BODY, - 'show_gravatar': settings.FORM_GRAVATAR_IN_COMMENTS, - 'showing': showing, - 'total': total, - 'more_comments_count' : int(total - showing), - 'show_latest_comments_first' : settings.SHOW_LATEST_COMMENTS_FIRST, - 'user': user, - } - -@register.inclusion_tag('node/comments.html') -def comments(post, user): - return _comments(post, user) - -@register.inclusion_tag("node/contributors_info.html", takes_context=True) -def contributors_info(context, node, verb=None): - return { - 'node_verb': verb and verb or ((node.node_type == "question") and _("asked") or ( - (node.node_type == "answer") and _("answered") or _("posted"))), - 'node': node, - 'context' : context - } - -@register.inclusion_tag("node/reviser_info.html") -def reviser_info(revision): - return {'revision': revision} +from datetime import datetime, timedelta +import re + +from forum.models import Question, Action +from django.template import Template, Context +from django.utils.translation import ungettext, ugettext as _ +from django.utils.html import strip_tags +from django.utils.encoding import smart_unicode +from django.utils.safestring import mark_safe +from django.conf import settings as django_settings +from django.core.urlresolvers import reverse +from django import template +from forum.actions import * +from forum import settings + +register = template.Library() + +@register.inclusion_tag('node/vote_buttons.html') +def vote_buttons(post, user): + context = dict(post=post, user_vote='none') + + if user.is_authenticated(): + context['user_vote'] = {1: 'up', -1: 'down', None: 'none'}[VoteAction.get_for(user, post)] + + return context + +@register.inclusion_tag('node/accept_button.html') +def accept_button(answer, user): + if not settings.DISABLE_ACCEPTING_FEATURE: + return { + 'can_accept': user.is_authenticated() and user.can_accept_answer(answer), + 'answer': answer, + 'user': user + } + else: + return '' + +@register.inclusion_tag('node/wiki_symbol.html') +def wiki_symbol(user, post): + context = { + 'is_wiki': post.nis.wiki, + 'post_type': post.friendly_name + } + + if post.nis.wiki: + if user.can_edit_post(post): + context['can_edit'] = True + context['edit_url'] = reverse('edit_' + post.node_type, kwargs={'id': post.id}) + context['by'] = post.nstate.wiki.by.username + context['at'] = post.nstate.wiki.at + + return context + +@register.inclusion_tag('node/favorite_mark.html') +def favorite_mark(question, user): + try: + FavoriteAction.objects.get(canceled=False, node=question, user=user) + favorited = True + except: + favorited = False + + return {'favorited': favorited, 'favorite_count': question.favorite_count, 'question': question} + +@register.simple_tag +def post_classes(post): + classes = [] + + if post.nis.deleted: + classes.append('deleted') + + if post.node_type == "answer": + if (not settings.DISABLE_ACCEPTING_FEATURE) and post.nis.accepted: + classes.append('accepted-answer') + + if post.author == post.question.author: + classes.append('answered-by-owner') + + return " ".join(classes) + +def post_control(text, url, command=False, withprompt=False, confirm=False, title="", copy=False, extra_classes=[]): + classes = (command and "ajax-command" or " ") + (withprompt and " withprompt" or " ") + (confirm and " confirm" or " ") + \ + (copy and " copy" or " ") + + for extra_class in extra_classes: + classes += " %s" % extra_class + + return {'text': text, 'url': url, 'classes': classes, 'title': title} + + +moderation_enabled = False +for m in django_settings.MODULE_LIST: + if m.__name__.endswith('moderation'): + moderation_enabled = True + +@register.inclusion_tag('node/post_controls.html' if not moderation_enabled else "modules/moderation/node/post_controls.html") +def post_controls(post, user): + controls = [] + menu = [] + post_type = post.node_type + + # We show the link tool if the post is an Answer. It is visible to Guests too. + if post_type == "answer": + # Answer permanent link tool + controls.append(post_control(_('permanent link'), reverse('answer_permanent_link', kwargs={'id' : post.id,}), + title=_("answer permanent link"), command=True, withprompt=True, copy=True)) + + # Users should be able to award points for an answer. Users cannot award their own answers + if user != post.author and user.is_authenticated() and user.reputation > 1: + controls.append(post_control(_("award points"), reverse('award_points', kwargs={'user_id' : post.author.id, + 'answer_id' : post.id}), title=_("award points to %s") % smart_unicode(post.author.username), + command=True, withprompt=True)) + + # The other controls are visible only to authenticated users. + if user.is_authenticated(): + try: + edit_url = reverse('edit_' + post_type, kwargs={'id': post.id}) + if user.can_edit_post(post): + controls.append(post_control(_('edit'), edit_url)) + elif post_type == 'question' and user.can_retag_questions(): + controls.append(post_control(_('retag'), edit_url)) + except: + pass + + if post_type == 'question': + if post.nis.closed and user.can_reopen_question(post): + controls.append(post_control(_('reopen'), reverse('reopen', kwargs={'id': post.id}), command=True)) + elif not post.nis.closed and user.can_close_question(post): + controls.append(post_control(_('close'), reverse('close', kwargs={'id': post.id}), command=True, withprompt=True)) + + if user.can_flag_offensive(post): + label = _('report') + + if user.can_view_offensive_flags(post): + label = "%s (%d)" % (label, post.flag_count) + + + report_control = post_control(label, reverse('flag_post', kwargs={'id': post.id}), + command=True, withprompt=True, + title=_("report as offensive (i.e containing spam, advertising, malicious text, etc.)")) + + # Depending on the setting choose where to attach the control + if settings.REPORT_OFFENSIVE_CONTROL_POSITION.value == "more": + menu.append(report_control) + else: + controls.append(report_control) + + if user.can_delete_post(post): + if post.nis.deleted: + controls.append(post_control(_('undelete'), reverse('delete_post', kwargs={'id': post.id}), + command=True, confirm=True)) + else: + controls.append(post_control(_('delete'), reverse('delete_post', kwargs={'id': post.id}), + command=True, confirm=True)) + + if user.can_delete_post(post): + menu.append(post_control(_('see revisions'), + reverse('revisions', + kwargs={'id': post.id}), + command=False, confirm=False)) + + if settings.WIKI_ON: + if (not post.nis.wiki) and user.can_wikify(post): + menu.append(post_control(_('mark as community wiki'), reverse('wikify', kwargs={'id': post.id}), + command=True, confirm=True)) + + elif post.nis.wiki and user.can_cancel_wiki(post): + menu.append(post_control(_('cancel community wiki'), reverse('wikify', kwargs={'id': post.id}), + command=True, confirm=True)) + + if post.node_type == "answer" and user.can_convert_to_comment(post): + menu.append(post_control(_('convert to comment'), reverse('convert_to_comment', kwargs={'id': post.id}), + command=True, withprompt=True)) + + if post.node_type == "answer" and user.can_convert_to_question(post): + menu.append(post_control(_('convert to question'), reverse('convert_to_question', kwargs={'id': post.id}), + command=False, confirm=True)) + + if user.is_superuser or user.is_staff: + plain_text = strip_tags(post.html) + + char_count = len(plain_text) + fullStr = plain_text + " " + left_trimmedStr = re.sub(re.compile(r"^[^\w]+", re.IGNORECASE), "", fullStr) + cleanedStr = re.sub(re.compile(r"[^\w]+", re.IGNORECASE), " ", left_trimmedStr) + splitString = cleanedStr.split(" ") + word_count = len(splitString) - 1 + + metrics = mark_safe("%s %s / %s %s" % (char_count, ungettext('character', 'characters', char_count), + word_count, ungettext('word', 'words', word_count))) + + menu.append(post_control(metrics, "#", command=False, withprompt=False)) + + return {'controls': controls, 'menu': menu, 'post': post, 'user': user} + +def _comments(post, user): + all_comments = post.comments.filter_state(deleted=False)\ + .order_by('-added_at' if settings.SHOW_LATEST_COMMENTS_FIRST else 'added_at') + + if len(all_comments) <= 5: + top_scorers = all_comments + else: + top_scorers = sorted(all_comments, lambda c1, c2: cmp(c2.score, c1.score))[0:5] + + comments = [] + showing = 0 + for c in all_comments: + context = { + 'can_delete': user.can_delete_comment(c), + 'can_like': user.can_like_comment(c), + 'can_edit': user.can_edit_comment(c), + 'can_convert': user.can_convert_comment_to_answer(c) + } + + if c in top_scorers or c.is_reply_to(user): + context['top_scorer'] = True + showing += 1 + + if context['can_like']: + context['likes'] = VoteAction.get_for(user, c) == 1 + + context['user'] = c.user + context['comment'] = c.comment + context.update(dict(c.__dict__)) + comments.append(context) + + # Generate canned comments + canned_comments = [] + for comment in settings.CANNED_COMMENTS: + t = Template(smart_unicode(comment)) + c = Context({ + 'post' : post, + 'settings' : settings, + }) + canned_comments.append(t.render(c)) + + total = len(all_comments) + return { + 'comments': comments, + 'canned_comments': canned_comments, + 'post': post, + 'can_comment': user.can_comment(post), + 'max_length': settings.FORM_MAX_COMMENT_BODY, + 'min_length': settings.FORM_MIN_COMMENT_BODY, + 'show_gravatar': settings.FORM_GRAVATAR_IN_COMMENTS, + 'showing': showing, + 'total': total, + 'more_comments_count' : int(total - showing), + 'show_latest_comments_first' : settings.SHOW_LATEST_COMMENTS_FIRST, + 'user': user, + } + +@register.inclusion_tag('node/comments.html') +def comments(post, user): + return _comments(post, user) + +@register.inclusion_tag("node/contributors_info.html", takes_context=True) +def contributors_info(context, node, verb=None): + return { + 'node_verb': verb and verb or ((node.node_type == "question") and _("asked") or ( + (node.node_type == "answer") and _("answered") or _("posted"))), + 'node': node, + 'context' : context + } + +@register.inclusion_tag("node/reviser_info.html") +def reviser_info(revision): + return {'revision': revision} diff --git a/forum/templatetags/question_list_tags.py b/forum/templatetags/question_list_tags.py index 2f2aef9..2447cd1 100644 --- a/forum/templatetags/question_list_tags.py +++ b/forum/templatetags/question_list_tags.py @@ -1,81 +1,81 @@ -from django import template -from django.utils.translation import ugettext as _ -from django.utils.safestring import mark_safe -from forum.models import Tag, MarkedTag -from forum.templatetags import argument_parser -from forum import settings - -register = template.Library() - -class QuestionItemNode(template.Node): - template = template.loader.get_template('question_list/item.html') - - def __init__(self, question, options): - self.question = template.Variable(question) - self.options = options - - def render(self, context): - return self.template.render(template.Context({ - 'question': self.question.resolve(context), - 'question_summary': self.options.get('question_summary', 'no' ) == 'yes', - 'favorite_count': self.options.get('favorite_count', 'no') == 'yes', - 'signature_type': self.options.get('signature_type', 'lite'), - })) - -class SubscriptionItemNode(template.Node): - template = template.loader.get_template('question_list/subscription_item.html') - - def __init__(self, subscription, question, options): - self.question = template.Variable(question) - self.subscription = template.Variable(subscription) - self.options = options - - def render(self, context): - return self.template.render(template.Context({ - 'question': self.question.resolve(context), - 'subscription': self.subscription.resolve(context), - 'signature_type': self.options.get('signature_type', 'lite'), - })) - -@register.tag -def question_list_item(parser, token): - tokens = token.split_contents()[1:] - return QuestionItemNode(tokens[0], argument_parser(tokens[1:])) - -@register.tag -def subscription_list_item(parser, token): - tokens = token.split_contents()[1:] - return SubscriptionItemNode(tokens[0], tokens[1], argument_parser(tokens[2:])) - -@register.inclusion_tag('question_list/sort_tabs.html') -def question_sort_tabs(sort_context): - return sort_context - -@register.inclusion_tag('question_list/related_tags.html') -def question_list_related_tags(questions): - if len(questions): - tags = Tag.objects.filter(nodes__id__in=[q.id for q in questions]).distinct() - - if settings.LIMIT_RELATED_TAGS: - tags = tags[:settings.LIMIT_RELATED_TAGS] - - return {'tags': tags} - else: - return {'tags': False} - -@register.inclusion_tag('question_list/tag_selector.html', takes_context=True) -def tag_selector(context): - request = context['request'] - show_interesting_tags = settings.SHOW_INTERESTING_TAGS_BOX - - if request.user.is_authenticated(): - pt = MarkedTag.objects.filter(user=request.user) - return { - 'request' : request, - "interesting_tag_names": pt.filter(reason='good').values_list('tag__name', flat=True), - 'ignored_tag_names': pt.filter(reason='bad').values_list('tag__name', flat=True), - 'user_authenticated': True, - 'show_interesting_tags' : show_interesting_tags, - } - else: - return { 'request' : request, 'user_authenticated': False, 'show_interesting_tags' : show_interesting_tags } +from django import template +from django.utils.translation import ugettext as _ +from django.utils.safestring import mark_safe +from forum.models import Tag, MarkedTag +from forum.templatetags import argument_parser +from forum import settings + +register = template.Library() + +class QuestionItemNode(template.Node): + template = template.loader.get_template('question_list/item.html') + + def __init__(self, question, options): + self.question = template.Variable(question) + self.options = options + + def render(self, context): + return self.template.render(template.Context({ + 'question': self.question.resolve(context), + 'question_summary': self.options.get('question_summary', 'no' ) == 'yes', + 'favorite_count': self.options.get('favorite_count', 'no') == 'yes', + 'signature_type': self.options.get('signature_type', 'lite'), + })) + +class SubscriptionItemNode(template.Node): + template = template.loader.get_template('question_list/subscription_item.html') + + def __init__(self, subscription, question, options): + self.question = template.Variable(question) + self.subscription = template.Variable(subscription) + self.options = options + + def render(self, context): + return self.template.render(template.Context({ + 'question': self.question.resolve(context), + 'subscription': self.subscription.resolve(context), + 'signature_type': self.options.get('signature_type', 'lite'), + })) + +@register.tag +def question_list_item(parser, token): + tokens = token.split_contents()[1:] + return QuestionItemNode(tokens[0], argument_parser(tokens[1:])) + +@register.tag +def subscription_list_item(parser, token): + tokens = token.split_contents()[1:] + return SubscriptionItemNode(tokens[0], tokens[1], argument_parser(tokens[2:])) + +@register.inclusion_tag('question_list/sort_tabs.html') +def question_sort_tabs(sort_context): + return sort_context + +@register.inclusion_tag('question_list/related_tags.html') +def question_list_related_tags(questions): + if len(questions): + tags = Tag.objects.filter(nodes__id__in=[q.id for q in questions]).distinct() + + if settings.LIMIT_RELATED_TAGS: + tags = tags[:settings.LIMIT_RELATED_TAGS] + + return {'tags': tags} + else: + return {'tags': False} + +@register.inclusion_tag('question_list/tag_selector.html', takes_context=True) +def tag_selector(context): + request = context['request'] + show_interesting_tags = settings.SHOW_INTERESTING_TAGS_BOX + + if request.user.is_authenticated(): + pt = MarkedTag.objects.filter(user=request.user) + return { + 'request' : request, + "interesting_tag_names": pt.filter(reason='good').values_list('tag__name', flat=True), + 'ignored_tag_names': pt.filter(reason='bad').values_list('tag__name', flat=True), + 'user_authenticated': True, + 'show_interesting_tags' : show_interesting_tags, + } + else: + return { 'request' : request, 'user_authenticated': False, 'show_interesting_tags' : show_interesting_tags } diff --git a/forum/templatetags/user_tags.py b/forum/templatetags/user_tags.py index 2bc4385..d16153d 100644 --- a/forum/templatetags/user_tags.py +++ b/forum/templatetags/user_tags.py @@ -1,74 +1,74 @@ -from django import template -from django.utils.translation import ugettext as _ -from django.utils.safestring import mark_safe -import logging - -register = template.Library() - -class UserSignatureNode(template.Node): - template = template.loader.get_template('users/signature.html') - - def __init__(self, user, format): - self.user = template.Variable(user) - self.format = template.Variable(format) - - def render(self, context): - return self.template.render(template.Context({ - 'user': self.user.resolve(context), - 'format': self.format.resolve(context) - })) - -@register.tag -def user_signature(parser, token): - try: - tag_name, user, format = token.split_contents() - except ValueError: - raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] - - return UserSignatureNode(user, format) - - -class ActivityNode(template.Node): - template = template.loader.get_template('users/activity.html') - - def __init__(self, activity, viewer): - self.activity = template.Variable(activity) - self.viewer = template.Variable(viewer) - - def render(self, context): - try: - action = self.activity.resolve(context).leaf - viewer = self.viewer.resolve(context) - describe = mark_safe(action.describe(viewer)) - return self.template.render(template.Context(dict(action=action, describe=describe))) - except Exception, e: - import traceback - msg = "Error in action describe: \n %s" % ( - traceback.format_exc() - ) - logging.error(msg) - -@register.tag -def activity_item(parser, token): - try: - tag_name, activity, viewer = token.split_contents() - except ValueError: - raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] - - return ActivityNode(activity, viewer) - - -@register.tag -def flagged_item(parser, token): - try: - tag_name, post, viewer = token.split_contents() - except ValueError: - raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] - - return ActivityNode(post, viewer) - - -@register.inclusion_tag('users/menu.html') -def user_menu(viewer, user): - return dict(viewer=viewer, user=user) - +from django import template +from django.utils.translation import ugettext as _ +from django.utils.safestring import mark_safe +import logging + +register = template.Library() + +class UserSignatureNode(template.Node): + template = template.loader.get_template('users/signature.html') + + def __init__(self, user, format): + self.user = template.Variable(user) + self.format = template.Variable(format) + + def render(self, context): + return self.template.render(template.Context({ + 'user': self.user.resolve(context), + 'format': self.format.resolve(context) + })) + +@register.tag +def user_signature(parser, token): + try: + tag_name, user, format = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] + + return UserSignatureNode(user, format) + + +class ActivityNode(template.Node): + template = template.loader.get_template('users/activity.html') + + def __init__(self, activity, viewer): + self.activity = template.Variable(activity) + self.viewer = template.Variable(viewer) + + def render(self, context): + try: + action = self.activity.resolve(context).leaf + viewer = self.viewer.resolve(context) + describe = mark_safe(action.describe(viewer)) + return self.template.render(template.Context(dict(action=action, describe=describe))) + except Exception, e: + import traceback + msg = "Error in action describe: \n %s" % ( + traceback.format_exc() + ) + logging.error(msg) + +@register.tag +def activity_item(parser, token): + try: + tag_name, activity, viewer = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] + + return ActivityNode(activity, viewer) + + +@register.tag +def flagged_item(parser, token): + try: + tag_name, post, viewer = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] + + return ActivityNode(post, viewer) + + +@register.inclusion_tag('users/menu.html') +def user_menu(viewer, user): + return dict(viewer=viewer, user=user) + diff --git a/forum/urls.py b/forum/urls.py index 380d6b3..22b8fab 100644 --- a/forum/urls.py +++ b/forum/urls.py @@ -2,7 +2,7 @@ import startup import os.path from forum import settings -from django.conf.urls.defaults import * +from django.conf.urls import patterns, url, include from django.conf import settings as djsettings from django.contrib import admin from forum import views as app @@ -18,11 +18,6 @@ sitemaps = { APP_PATH = os.path.dirname(__file__) -try: - admin_url = url(r'^%s(.*)' % _('nimda/'), admin.site.root) -except AttributeError: - admin_url = url(r'^%s(.*)' % _('nimda/'), admin.site.urls) - # Choose the user urls pattern if bool(settings.INCLUDE_ID_IN_USER_URLS.value): core_user_urls_prefix = r'^%s(?P\d+)/(?P.*)' @@ -30,7 +25,8 @@ else: core_user_urls_prefix = r'^%s(?P.*)' core_urls = ( - url(r'^$', app.readers.index, name='index'), admin_url, + url(r'^$', app.readers.index, name='index'), + url(r'^%s(.*)' % _('nimda/'), admin.site.urls), url(r'^sitemap.xml$', 'forum.sitemap.index', {'sitemaps': sitemaps}), url(r'^sitemap-(?P
          .+)-(?P\d+)\.xml$', 'forum.sitemap.sitemap', {'sitemaps': sitemaps}, name="sitemap_section_page"), @@ -85,7 +81,7 @@ core_urls = ( url(r'^%s(?P\d+)/' % _('convert_to_question/'), app.writers.convert_to_question,name='convert_to_question'), url(r'^%s(?P\d+)/' % _('wikify/'), app.commands.wikify, name='wikify'), - url(r'^%s(?P\d+)/(?P[\w-]*)$' % _('question/'), 'django.views.generic.simple.redirect_to', {'url': '/questions/%(id)s/%(slug)s'}), + url(r'^%s(?P\d+)/(?P[\w-]*)$' % _('question/'), 'django.shortcuts.redirect', {'url': '/questions/%(id)s/%(slug)s'}), url(r'^%s(?P\d+)/?$' % _('questions/'), app.readers.question, name='question'), url(r'^%s(?P\d+)/(?P.*)/(?P\d+)$' % _('questions/'), app.readers.question), url(r'^%s(?P\d+)/(?P.*)$' % _('questions/'), app.readers.question, name='question'), diff --git a/forum/user_messages/context_processors.py b/forum/user_messages/context_processors.py index 5f7b857..b068137 100644 --- a/forum/user_messages/context_processors.py +++ b/forum/user_messages/context_processors.py @@ -6,50 +6,11 @@ Time-stamp: <2008-07-19 23:16:19 carljm context_processors.py> """ from django.utils.encoding import StrAndUnicode -from forum.user_messages import get_and_delete_messages +from django.contrib.messages.api import get_messages def user_messages (request): """ Returns session messages for the current session. """ - messages = request.user.get_and_delete_messages() - #if request.user.is_authenticated(): - #else: - # messages = LazyMessages(request) - #import inspect - #print inspect.stack()[1] - #print messages - return { 'user_messages': messages } - -class LazyMessages (StrAndUnicode): - """ - Lazy message container, so messages aren't actually retrieved from - session and deleted until the template asks for them. - - """ - def __init__(self, request): - self.request = request - - def __iter__(self): - return iter(self.messages) - - def __len__(self): - return len(self.messages) - - def __nonzero__(self): - return bool(self.messages) - - def __unicode__(self): - return unicode(self.messages) - - def __getitem__(self, *args, **kwargs): - return self.messages.__getitem__(*args, **kwargs) - - def _get_messages(self): - if hasattr(self, '_messages'): - return self._messages - self._messages = get_and_delete_messages(self.request) - return self._messages - messages = property(_get_messages) - + return { 'user_messages': get_messages(request) } diff --git a/forum/utils/decorators.py b/forum/utils/decorators.py index e4e7acb..6cc2d5b 100644 --- a/forum/utils/decorators.py +++ b/forum/utils/decorators.py @@ -1,13 +1,13 @@ +import json from django.http import HttpResponse, HttpResponseForbidden, Http404 -from django.utils import simplejson def ajax_login_required(view_func): def wrap(request,*args,**kwargs): if request.user.is_authenticated(): return view_func(request,*args,**kwargs) else: - json = simplejson.dumps({'login_required':True}) - return HttpResponseForbidden(json,mimetype='application/json') + json = json.dumps({'login_required':True}) + return HttpResponseForbidden(json, mimetype='application/json') return wrap def ajax_method(view_func): @@ -19,7 +19,7 @@ def ajax_method(view_func): retval.mimetype = 'application/json' return retval else: - json = simplejson.dumps(retval) - return HttpResponse(json,mimetype='application/json') + json = json.dumps(retval) + return HttpResponse(json, mimetype='application/json') return wrap diff --git a/forum/utils/html.py b/forum/utils/html.py index 256a2d8..88253e1 100644 --- a/forum/utils/html.py +++ b/forum/utils/html.py @@ -1,5 +1,4 @@ """Utilities for working with HTML.""" -#import html5lib from html5lib import sanitizer, serializer, tokenizer, treebuilders, treewalkers, HTMLParser from urllib import quote_plus from django.utils.html import strip_tags diff --git a/forum/utils/html2text.py b/forum/utils/html2text.py index 3b51771..c666610 100644 --- a/forum/utils/html2text.py +++ b/forum/utils/html2text.py @@ -1,140 +1,140 @@ -# Copyright (c) 2001 Chris Withers -# -# This Software is released under the MIT License: -# http://www.opensource.org/licenses/mit-license.html -# See license.txt for more details. -# -# $Id: html2text.py,v 1.7 2002/12/17 16:56:17 fresh Exp $ - -import sgmllib -from string import lower, replace, split, join - -class HTML2Text(sgmllib.SGMLParser): - - from htmlentitydefs import entitydefs # replace entitydefs from sgmllib - - def __init__(self, ignore_tags=(), indent_width=4, page_width=80): - sgmllib.SGMLParser.__init__(self) - self.result = "" - self.indent = 0 - self.ol_number = 0 - self.page_width=page_width - self.inde_width=indent_width - self.lines=[] - self.line=[] - self.ignore_tags = ignore_tags - - def add_text(self,text): - # convert text into words - words = split(replace(text,'\n',' ')) - self.line.extend(words) - - def add_break(self): - self.lines.append((self.indent,self.line)) - self.line=[] - - def generate(self): - # join lines with indents - indent_width = self.inde_width - page_width = self.page_width - out_paras=[] - for indent,line in self.lines+[(self.indent,self.line)]: - - i=indent*indent_width - indent_string = i*' ' - line_width = page_width-i - - out_para='' - out_line=[] - len_out_line=0 - for word in line: - len_word = len(word) - if len_out_line+len_word" + suspension.extra.get(msg_type, '')) - request.user.message_set.create(message) + messages.info(request, message) return HttpResponseRedirect(reverse('index')) @decorate.withfn(login_required) diff --git a/forum/views/commands.py b/forum/views/commands.py index 5271eec..62d864f 100644 --- a/forum/views/commands.py +++ b/forum/views/commands.py @@ -1,18 +1,20 @@ # -*- coding: utf-8 -*- import datetime +import json import logging from urllib import urlencode from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse -from django.utils import simplejson from django.utils.encoding import smart_unicode from django.utils.translation import ungettext, ugettext as _ from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.shortcuts import get_object_or_404, render_to_response +from django.contrib import messages + from forum.models import * from forum.utils.decorators import ajax_login_required from forum.actions import * @@ -353,7 +355,7 @@ def accept_answer(request, id): """) # Notify the user with a message that an answer has been accepted - request.user.message_set.create(message=msg) + messages.info(request, msg) # Redirect URL should include additional get parameters that might have been attached redirect_url = answer.parent.get_absolute_url() + "?accepted_answer=true&%s" % smart_unicode(urlencode(request.GET)) @@ -545,7 +547,7 @@ def mark_tag(request, tag=None, **kwargs):#tagging system pass else: ts.update(reason=reason) - return HttpResponse(simplejson.dumps(''), mimetype="application/json") + return HttpResponse(json.dumps(''), mimetype="application/json") def matching_tags(request): if len(request.GET['q']) == 0: @@ -577,7 +579,7 @@ def related_questions(request): if can_rank and isinstance(can_rank, basestring): questions = questions.order_by(can_rank) - return HttpResponse(simplejson.dumps( + return HttpResponse(json.dumps( [dict(title=q.title, url=q.get_absolute_url(), score=q.score, summary=q.summary) for q in questions.filter_state(deleted=False)[0:10]]), mimetype="application/json") else: @@ -638,3 +640,4 @@ def award_points(request, user_id, answer_id): AwardPointsAction(user=request.user, node=answer, extra=extra).save(data=dict(value=points, affected=awarded_user)) return { 'message' : _("You have awarded %(awarded_user)s with %(points)d points") % {'awarded_user' : awarded_user, 'points' : points} } + diff --git a/forum/views/decorators.py b/forum/views/decorators.py index 9cb8cbd..3bede66 100644 --- a/forum/views/decorators.py +++ b/forum/views/decorators.py @@ -1,84 +1,83 @@ -# -*- coding: utf-8 -*- - -import logging - -from datetime import datetime - -from django.http import HttpResponse, HttpResponseRedirect -from django.utils import simplejson -from django.shortcuts import render_to_response -from django.core.urlresolvers import reverse -from django.template import RequestContext -from django.utils.translation import ugettext as _ - -from forum.modules import ui, decorate -from forum.settings import ONLINE_USERS - -def login_required(func, request, *args, **kwargs): - if not request.user.is_authenticated(): - return HttpResponseRedirect(reverse('auth_signin')) - else: - return func(request, *args, **kwargs) - -def render(template=None, tab=None, tab_title='', weight=500, tabbed=True): - def decorator(func): - def decorated(context, request, *args, **kwargs): - if request.user.is_authenticated(): - ONLINE_USERS[request.user] = datetime.now() - - if isinstance(context, HttpResponse): - return context - - if tab is not None: - context['tab'] = tab - - return render_to_response(context.pop('template', template), context, - context_instance=RequestContext(request)) - - if tabbed and tab and tab_title: - ui.register(ui.PAGE_TOP_TABS, - ui.PageTab(tab, tab_title, lambda: reverse(func.__name__), weight=weight)) - - return decorate.result.withfn(decorated, needs_params=True)(func) - - return decorator - -class CommandException(Exception): - pass - -class RefreshPageCommand(HttpResponse): - def __init__(self): - super(RefreshPageCommand, self).__init__( - content=simplejson.dumps({'commands': {'refresh_page': []}, 'success': True}), - mimetype="application/json") - -def command(func, request, *args, **kwargs): - try: - response = func(request, *args, **kwargs) - - if isinstance(response, HttpResponse): - return response - - response['success'] = True - except Exception, e: - import traceback - #traceback.print_exc() - - if isinstance(e, CommandException): - response = { - 'success': False, - 'error_message': e.message - } - else: - logging.error("%s: %s" % (func.__name__, str(e))) - logging.error(traceback.format_exc()) - response = { - 'success': False, - 'error_message': _("We're sorry, but an unknown error ocurred.
          Please try again in a while.") - } - - if request.is_ajax(): - return HttpResponse(simplejson.dumps(response), mimetype="application/json") - else: - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) - +# -*- coding: utf-8 -*- +import json +import logging + +from datetime import datetime + +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render_to_response +from django.core.urlresolvers import reverse +from django.template import RequestContext +from django.utils.translation import ugettext as _ + +from forum.modules import ui, decorate +from forum.settings import ONLINE_USERS + +def login_required(func, request, *args, **kwargs): + if not request.user.is_authenticated(): + return HttpResponseRedirect(reverse('auth_signin')) + else: + return func(request, *args, **kwargs) + +def render(template=None, tab=None, tab_title='', weight=500, tabbed=True): + def decorator(func): + def decorated(context, request, *args, **kwargs): + if request.user.is_authenticated(): + ONLINE_USERS[request.user] = datetime.now() + + if isinstance(context, HttpResponse): + return context + + if tab is not None: + context['tab'] = tab + + return render_to_response(context.pop('template', template), context, + context_instance=RequestContext(request)) + + if tabbed and tab and tab_title: + ui.register(ui.PAGE_TOP_TABS, + ui.PageTab(tab, tab_title, lambda: reverse(func.__name__), weight=weight)) + + return decorate.result.withfn(decorated, needs_params=True)(func) + + return decorator + +class CommandException(Exception): + pass + +class RefreshPageCommand(HttpResponse): + def __init__(self): + super(RefreshPageCommand, self).__init__( + content=json.dumps({'commands': {'refresh_page': []}, 'success': True}), + mimetype="application/json") + +def command(func, request, *args, **kwargs): + try: + response = func(request, *args, **kwargs) + + if isinstance(response, HttpResponse): + return response + + response['success'] = True + except Exception, e: + import traceback + #traceback.print_exc() + + if isinstance(e, CommandException): + response = { + 'success': False, + 'error_message': e.message + } + else: + logging.error("%s: %s" % (func.__name__, str(e))) + logging.error(traceback.format_exc()) + response = { + 'success': False, + 'error_message': _("We're sorry, but an unknown error ocurred.
          Please try again in a while.") + } + + if request.is_ajax(): + return HttpResponse(json.dumps(response), mimetype="application/json") + else: + return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + diff --git a/forum/views/meta.py b/forum/views/meta.py index c9548e5..a47cf1d 100644 --- a/forum/views/meta.py +++ b/forum/views/meta.py @@ -9,13 +9,14 @@ from django.views.decorators.cache import cache_page from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe +from django.contrib import messages + from forum import settings from forum.views.decorators import login_required from forum.forms import FeedbackForm from forum.modules import decorate from forum.forms import get_next_url from forum.models import Badge, Award, User, Page -from forum.badges.base import BadgesMeta from forum.http_responses import HttpResponseNotFound, HttpResponseIntServerError from forum.utils.mail import send_template_email from forum.templatetags.extra_filters import or_preview @@ -68,7 +69,7 @@ def feedback(request): send_template_email(recipients, "notifications/feedback.html", context) msg = _('Thanks for the feedback!') - request.user.message_set.create(message=msg) + messages.info(request, msg) return HttpResponseRedirect(get_next_url(request)) else: form = FeedbackForm(request.user, initial={'next':get_next_url(request)}) @@ -88,6 +89,7 @@ def logout(request): @decorators.render('badges.html', 'badges', _('badges'), weight=300) def badges(request): + from forum.badges.base import BadgesMeta badges = sorted([Badge.objects.get(id=id) for id in BadgesMeta.by_id.keys()], lambda b1, b2: cmp(b1.name, b2.name)) if request.user.is_authenticated(): diff --git a/forum/views/users.py b/forum/views/users.py index dc865a2..c6fae00 100644 --- a/forum/views/users.py +++ b/forum/views/users.py @@ -1,484 +1,486 @@ -from forum.models import User -from django.db.models import Q, Count -from django.core.paginator import Paginator, EmptyPage, InvalidPage -from django.template.defaultfilters import slugify -from django.contrib.contenttypes.models import ContentType -from django.core.urlresolvers import reverse -from django.shortcuts import render_to_response, get_object_or_404 -from django.template import RequestContext -from django.http import HttpResponse, HttpResponseRedirect, Http404 -from forum.http_responses import HttpResponseUnauthorized -from django.utils.translation import ugettext as _ -from django.utils.http import urlquote_plus -from django.utils.html import strip_tags -from django.utils.encoding import smart_unicode -from django.utils import simplejson -from django.core.urlresolvers import reverse, NoReverseMatch -from forum.forms import * -from forum.utils.html import sanitize_html -from forum.modules import decorate, ReturnImediatelyException -from datetime import datetime, date -from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction, ReportAction -from forum.modules import ui -from forum.utils import pagination -from forum.views.readers import QuestionListPaginatorContext, AnswerPaginatorContext -from forum.settings import ONLINE_USERS - -import time -import datetime -import decorators - -class UserReputationSort(pagination.SimpleSort): - def apply(self, objects): - return objects.order_by('-is_active', self.order_by) - -class UserListPaginatorContext(pagination.PaginatorContext): - def __init__(self, pagesizes=(20, 35, 60), default_pagesize=35): - super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=( - (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))), - (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))), - (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))), - (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))), - ), pagesizes=pagesizes, default_pagesize=default_pagesize) - -class SubscriptionListPaginatorContext(pagination.PaginatorContext): - def __init__(self): - super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20) - -class UserAnswersPaginatorContext(pagination.PaginatorContext): - def __init__(self): - super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=( - (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))), - (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))), - (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))), - ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers')) - -USERS_PAGE_SIZE = 35# refactor - move to some constants file - -@decorators.render('users/users.html', 'users', _('users'), weight=200) -def users(request): - suser = request.REQUEST.get('q', "") - users = User.objects.all() - - if suser != "": - users = users.filter(username__icontains=suser) - - return pagination.paginated(request, ('users', UserListPaginatorContext()), { - "users" : users, - "suser" : suser, - }) - - -@decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200, tabbed=False) -def online_users(request): - suser = request.REQUEST.get('q', "") - - sort = "" - if request.GET.get("sort", None): - try: - sort = int(request.GET["sort"]) - except ValueError: - logging.error('Found invalid sort "%s", loading %s, refered by %s' % ( - request.GET.get("sort", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') - )) - raise Http404() - - page = 0 - if request.GET.get("page", None): - try: - page = int(request.GET["page"]) - except ValueError: - logging.error('Found invalid page "%s", loading %s, refered by %s' % ( - request.GET.get("page", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') - )) - raise Http404() - - pagesize = 10 - if request.GET.get("pagesize", None): - try: - pagesize = int(request.GET["pagesize"]) - except ValueError: - logging.error('Found invalid pagesize "%s", loading %s, refered by %s' % ( - request.GET.get("pagesize", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') - )) - raise Http404() - - - users = None - if sort == "reputation": - users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.reputation) - elif sort == "newest" : - users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest) - elif sort == "last": - users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.last) - elif sort == "name": - users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.name) - elif sort == "oldest": - users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.oldest) - elif sort == "newest": - users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest) - elif sort == "votes": - users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.votes) - else: - users = sorted(ONLINE_USERS.iteritems(), key=lambda x: x[1]) - - return render_to_response('users/online_users.html', { - "users" : users, - "suser" : suser, - "sort" : sort, - "page" : page, - "pageSize" : pagesize, - }) - - -def edit_user(request, id, slug): - user = get_object_or_404(User, id=id) - if not (request.user.is_superuser or request.user == user): - return HttpResponseUnauthorized(request) - if request.method == "POST": - form = EditUserForm(user, request.POST) - if form.is_valid(): - new_email = sanitize_html(form.cleaned_data['email']) - - if new_email != user.email: - user.email = new_email - user.email_isvalid = False - - try: - hash = ValidationHash.objects.get(user=request.user, type='email') - hash.delete() - except: - pass - - if settings.EDITABLE_SCREEN_NAME: - user.username = sanitize_html(form.cleaned_data['username']) - user.real_name = sanitize_html(form.cleaned_data['realname']) - user.website = sanitize_html(form.cleaned_data['website']) - user.location = sanitize_html(form.cleaned_data['city']) - user.date_of_birth = form.cleaned_data['birthday'] - if user.date_of_birth == "None": - user.date_of_birth = datetime(1900, 1, 1, 0, 0) - user.about = sanitize_html(form.cleaned_data['about']) - - user.save() - EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save() - - request.user.message_set.create(message=_("Profile updated.")) - return HttpResponseRedirect(user.get_profile_url()) - else: - form = EditUserForm(user) - return render_to_response('users/edit.html', { - 'user': user, - 'form' : form, - 'gravatar_faq_url' : reverse('faq') + '#gravatar', - }, context_instance=RequestContext(request)) - - -@decorate.withfn(decorators.command) -def user_powers(request, id, action, status): - if not request.user.is_superuser: - raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions.")) - - if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner(): - raise decorators.CommandException(_("Only the site owner can remove the super user status from other user.")) - - user = get_object_or_404(User, id=id) - new_state = action == 'grant' - - if status == 'super': - user.is_superuser = new_state - elif status == 'staff': - user.is_staff = new_state - else: - raise Http404() - - user.save() - return decorators.RefreshPageCommand() - - -@decorate.withfn(decorators.command) -def award_points(request, id): - if not request.POST: - return render_to_response('users/karma_bonus.html') - - if not request.user.is_superuser: - raise decorators.CommandException(_("Only superusers are allowed to award reputation points")) - - try: - points = int(request.POST['points']) - except: - raise decorators.CommandException(_("Invalid number of points to award.")) - - user = get_object_or_404(User, id=id) - - extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points) - - BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user)) - - return {'commands': { - 'update_profile_karma': [user.reputation] - }} - - -@decorate.withfn(decorators.command) -def suspend(request, id): - user = get_object_or_404(User, id=id) - - if not request.user.is_superuser: - raise decorators.CommandException(_("Only superusers can suspend other users")) - - if not request.POST.get('bantype', None): - if user.is_suspended(): - suspension = user.suspension - suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR']) - return decorators.RefreshPageCommand() - else: - return render_to_response('users/suspend_user.html') - - data = { - 'bantype': request.POST.get('bantype', 'Indefinitely').strip(), - 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')), - 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''), - 'suspended': user - } - - if data['bantype'] == 'forxdays': - try: - data['forxdays'] = int(request.POST['forxdays']) - except: - raise decorators.CommandException(_('Invalid numeric argument for the number of days.')) - - SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data) - - return decorators.RefreshPageCommand() - -@decorate.withfn(decorators.command) -def report_user(request, id): - user = get_object_or_404(User, id=id) - - if not request.POST.get('publicmsg', None): - return render_to_response('users/report_user.html') - - data = { - 'publicmsg': request.POST.get('publicmsg', _('N/A')), - 'reported': user - } - - ReportAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data) - - - return decorators.RefreshPageCommand() - - - -def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500): - def decorator(fn): - def params(request, id=None, slug=None): - # Get the user object by id if the id parameter has been passed - if id is not None: - user = get_object_or_404(User, id=id) - # ...or by slug if the slug has been given - elif slug is not None: - try: - user = User.objects.get(username__iexact=slug) - except User.DoesNotExist: - raise Http404 - - if private and not (user == request.user or request.user.is_superuser): - raise ReturnImediatelyException(HttpResponseUnauthorized(request)) - - if render_to and (not render_to(user)): - raise ReturnImediatelyException(HttpResponseRedirect(user.get_profile_url())) - - return [request, user], { 'slug' : slug, } - - decorated = decorate.params.withfn(params)(fn) - - def result(context_or_response, request, user, **kwargs): - rev_page_title = smart_unicode(user.username) + " - " + tab_description - - # Check whether the return type of the decorated function is a context or Http Response - if isinstance(context_or_response, HttpResponse): - response = context_or_response - - # If it is a response -- show it - return response - else: - # ...if it is a context move forward, update it and render it to response - context = context_or_response - - context.update({ - "tab": "users", - "active_tab" : tab_name, - "tab_description" : tab_description, - "page_title" : rev_page_title, - "can_view_private": (user == request.user) or request.user.is_superuser - }) - return render_to_response(template, context, context_instance=RequestContext(request)) - - decorated = decorate.result.withfn(result, needs_params=True)(decorated) - - if tabbed: - def url_getter(vu): - try: - return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(smart_unicode(vu.username))}) - except NoReverseMatch: - try: - return reverse(fn.__name__, kwargs={'id': vu.id}) - except NoReverseMatch: - return reverse(fn.__name__, kwargs={'slug': slugify(smart_unicode(vu.username))}) - - ui.register(ui.PROFILE_TABS, ui.ProfileTab( - tab_name, tab_title, tab_description,url_getter, private, render_to, weight - )) - - return decorated - return decorator - - -@user_view('users/stats.html', 'stats', _('overview'), _('user overview')) -def user_profile(request, user, **kwargs): - questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at') - answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at') - - # Check whether the passed slug matches the one for the user object - slug = kwargs['slug'] - if slug != slugify(smart_unicode(user.username)): - return HttpResponseRedirect(user.get_absolute_url()) - - up_votes = user.vote_up_count - down_votes = user.vote_down_count - votes_today = user.get_vote_count_today() - votes_total = user.can_vote_count_today() - - user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \ - .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count') - - awards = [(Badge.objects.get(id=b['id']), b['count']) for b in - Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')] - - return pagination.paginated(request, ( - ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), default_pagesize=15)), - ('answers', UserAnswersPaginatorContext())), { - "view_user" : user, - "questions" : questions, - "answers" : answers, - "up_votes" : up_votes, - "down_votes" : down_votes, - "total_votes": up_votes + down_votes, - "votes_today_left": votes_total-votes_today, - "votes_total_per_day": votes_total, - "user_tags" : user_tags[:50], - "awards": awards, - "total_awards" : len(awards), - }) - -@user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity')) -def user_recent(request, user, **kwargs): - activities = user.actions.exclude( - action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by( - '-action_date')[:USERS_PAGE_SIZE] - - return {"view_user" : user, "activities" : activities} - - -@user_view('users/reputation.html', 'reputation', _('reputation history'), _('graph of user karma')) -def user_reputation(request, user, **kwargs): - rep = list(user.reputes.order_by('date')) - values = [r.value for r in rep] - redux = lambda x, y: x+y - - graph_data = simplejson.dumps([ - (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i+1], 0)) - for i in range(len(values)) - ]) - - rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20] - - return {"view_user": user, "reputation": rep, "graph_data": graph_data} - -@user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True) -def user_votes(request, user, **kwargs): - votes = user.votes.exclude(node__state_string__contains="(deleted").filter( - node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE] - - return {"view_user" : user, "votes" : votes} - -@user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite')) -def user_favorites(request, user, **kwargs): - favorites = FavoriteAction.objects.filter(canceled=False, user=user) - - return {"favorites" : favorites, "view_user" : user} - -@user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False) -def user_subscriptions(request, user, **kwargs): - return _user_subscriptions(request, user, **kwargs) - -def _user_subscriptions(request, user, **kwargs): - enabled = True - - tab = request.GET.get('tab', "settings") - - # Manage tab - if tab == 'manage': - manage_open = True - - auto = request.GET.get('auto', 'True') - if auto == 'True': - show_auto = True - subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view') - else: - show_auto = False - subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view') - - return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), { - 'subscriptions':subscriptions, - 'view_user':user, - "auto":show_auto, - 'manage_open':manage_open, - }) - # Settings Tab and everything else - else: - manage_open = False - if request.method == 'POST': - manage_open = False - form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings) - - if form.is_valid(): - form.save() - message = _('New subscription settings are now saved') - - user.subscription_settings.enable_notifications = enabled - user.subscription_settings.save() - - request.user.message_set.create(message=message) - else: - form = SubscriptionSettingsForm(instance=user.subscription_settings) - - return { - 'view_user':user, - 'notificatons_on': enabled, - 'form':form, - 'manage_open':manage_open, - } - -@user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False) -def user_preferences(request, user, **kwargs): - if request.POST: - form = UserPreferencesForm(request.POST) - - if form.is_valid(): - user.prop.preferences = form.cleaned_data - request.user.message_set.create(message=_('New preferences saved')) - - else: - preferences = user.prop.preferences - - if preferences: - form = UserPreferencesForm(initial=preferences) - else: - form = UserPreferencesForm() - - return {'view_user': user, 'form': form} - - +from forum.models import User +from django.db.models import Q, Count +from django.core.paginator import Paginator, EmptyPage, InvalidPage +from django.template.defaultfilters import slugify +from django.contrib.contenttypes.models import ContentType +from django.core.urlresolvers import reverse +from django.shortcuts import render_to_response, get_object_or_404 +from django.template import RequestContext +from django.http import HttpResponse, HttpResponseRedirect, Http404 +from forum.http_responses import HttpResponseUnauthorized +from django.utils.translation import ugettext as _ +from django.utils.http import urlquote_plus +from django.utils.html import strip_tags +from django.utils.encoding import smart_unicode +from django.core.urlresolvers import reverse, NoReverseMatch +from forum.forms import * +from forum.utils.html import sanitize_html +from forum.modules import decorate, ReturnImediatelyException +from datetime import datetime, date +from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction, ReportAction +from forum.modules import ui +from forum.utils import pagination +from forum.views.readers import QuestionListPaginatorContext, AnswerPaginatorContext +from forum.settings import ONLINE_USERS + +from django.contrib import messages + +import json +import time +import datetime +import decorators + +class UserReputationSort(pagination.SimpleSort): + def apply(self, objects): + return objects.order_by('-is_active', self.order_by) + +class UserListPaginatorContext(pagination.PaginatorContext): + def __init__(self, pagesizes=(20, 35, 60), default_pagesize=35): + super (UserListPaginatorContext, self).__init__('USERS_LIST', sort_methods=( + (_('reputation'), UserReputationSort(_('reputation'), '-reputation', _("sorted by reputation"))), + (_('newest'), pagination.SimpleSort(_('recent'), '-date_joined', _("newest members"))), + (_('last'), pagination.SimpleSort(_('oldest'), 'date_joined', _("oldest members"))), + (_('name'), pagination.SimpleSort(_('by username'), 'username', _("sorted by username"))), + ), pagesizes=pagesizes, default_pagesize=default_pagesize) + +class SubscriptionListPaginatorContext(pagination.PaginatorContext): + def __init__(self): + super (SubscriptionListPaginatorContext, self).__init__('SUBSCRIPTION_LIST', pagesizes=(5, 10, 20), default_pagesize=20) + +class UserAnswersPaginatorContext(pagination.PaginatorContext): + def __init__(self): + super (UserAnswersPaginatorContext, self).__init__('USER_ANSWER_LIST', sort_methods=( + (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))), + (_('newest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))), + (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))), + ), default_sort=_('votes'), pagesizes=(5, 10, 20), default_pagesize=20, prefix=_('answers')) + +USERS_PAGE_SIZE = 35# refactor - move to some constants file + +@decorators.render('users/users.html', 'users', _('users'), weight=200) +def users(request): + suser = request.REQUEST.get('q', "") + users = User.objects.all() + + if suser != "": + users = users.filter(username__icontains=suser) + + return pagination.paginated(request, ('users', UserListPaginatorContext()), { + "users" : users, + "suser" : suser, + }) + + +@decorators.render('users/online_users.html', 'online_users', _('Online Users'), weight=200, tabbed=False) +def online_users(request): + suser = request.REQUEST.get('q', "") + + sort = "" + if request.GET.get("sort", None): + try: + sort = int(request.GET["sort"]) + except ValueError: + logging.error('Found invalid sort "%s", loading %s, refered by %s' % ( + request.GET.get("sort", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') + )) + raise Http404() + + page = 0 + if request.GET.get("page", None): + try: + page = int(request.GET["page"]) + except ValueError: + logging.error('Found invalid page "%s", loading %s, refered by %s' % ( + request.GET.get("page", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') + )) + raise Http404() + + pagesize = 10 + if request.GET.get("pagesize", None): + try: + pagesize = int(request.GET["pagesize"]) + except ValueError: + logging.error('Found invalid pagesize "%s", loading %s, refered by %s' % ( + request.GET.get("pagesize", ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') + )) + raise Http404() + + + users = None + if sort == "reputation": + users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.reputation) + elif sort == "newest" : + users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest) + elif sort == "last": + users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.last) + elif sort == "name": + users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.name) + elif sort == "oldest": + users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.oldest) + elif sort == "newest": + users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.newest) + elif sort == "votes": + users = sorted(ONLINE_USERS.sets.keys(), key=lambda user: user.votes) + else: + users = sorted(ONLINE_USERS.iteritems(), key=lambda x: x[1]) + + return render_to_response('users/online_users.html', { + "users" : users, + "suser" : suser, + "sort" : sort, + "page" : page, + "pageSize" : pagesize, + }) + + +def edit_user(request, id, slug): + user = get_object_or_404(User, id=id) + if not (request.user.is_superuser or request.user == user): + return HttpResponseUnauthorized(request) + if request.method == "POST": + form = EditUserForm(user, request.POST) + if form.is_valid(): + new_email = sanitize_html(form.cleaned_data['email']) + + if new_email != user.email: + user.email = new_email + user.email_isvalid = False + + try: + hash = ValidationHash.objects.get(user=request.user, type='email') + hash.delete() + except: + pass + + if settings.EDITABLE_SCREEN_NAME: + user.username = sanitize_html(form.cleaned_data['username']) + user.real_name = sanitize_html(form.cleaned_data['realname']) + user.website = sanitize_html(form.cleaned_data['website']) + user.location = sanitize_html(form.cleaned_data['city']) + user.date_of_birth = form.cleaned_data['birthday'] + if user.date_of_birth == "None": + user.date_of_birth = datetime(1900, 1, 1, 0, 0) + user.about = sanitize_html(form.cleaned_data['about']) + + user.save() + EditProfileAction(user=user, ip=request.META['REMOTE_ADDR']).save() + + messages.info(request, _("Profile updated.")) + return HttpResponseRedirect(user.get_profile_url()) + else: + form = EditUserForm(user) + return render_to_response('users/edit.html', { + 'user': user, + 'form' : form, + 'gravatar_faq_url' : reverse('faq') + '#gravatar', + }, context_instance=RequestContext(request)) + + +@decorate.withfn(decorators.command) +def user_powers(request, id, action, status): + if not request.user.is_superuser: + raise decorators.CommandException(_("Only superusers are allowed to alter other users permissions.")) + + if (action == 'remove' and 'status' == 'super') and not request.user.is_siteowner(): + raise decorators.CommandException(_("Only the site owner can remove the super user status from other user.")) + + user = get_object_or_404(User, id=id) + new_state = action == 'grant' + + if status == 'super': + user.is_superuser = new_state + elif status == 'staff': + user.is_staff = new_state + else: + raise Http404() + + user.save() + return decorators.RefreshPageCommand() + + +@decorate.withfn(decorators.command) +def award_points(request, id): + if not request.POST: + return render_to_response('users/karma_bonus.html') + + if not request.user.is_superuser: + raise decorators.CommandException(_("Only superusers are allowed to award reputation points")) + + try: + points = int(request.POST['points']) + except: + raise decorators.CommandException(_("Invalid number of points to award.")) + + user = get_object_or_404(User, id=id) + + extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points) + + BonusRepAction(user=request.user, extra=extra).save(data=dict(value=points, affected=user)) + + return {'commands': { + 'update_profile_karma': [user.reputation] + }} + + +@decorate.withfn(decorators.command) +def suspend(request, id): + user = get_object_or_404(User, id=id) + + if not request.user.is_superuser: + raise decorators.CommandException(_("Only superusers can suspend other users")) + + if not request.POST.get('bantype', None): + if user.is_suspended(): + suspension = user.suspension + suspension.cancel(user=request.user, ip=request.META['REMOTE_ADDR']) + return decorators.RefreshPageCommand() + else: + return render_to_response('users/suspend_user.html') + + data = { + 'bantype': request.POST.get('bantype', 'Indefinitely').strip(), + 'publicmsg': request.POST.get('publicmsg', _('Bad behaviour')), + 'privatemsg': request.POST.get('privatemsg', None) or request.POST.get('publicmsg', ''), + 'suspended': user + } + + if data['bantype'] == 'forxdays': + try: + data['forxdays'] = int(request.POST['forxdays']) + except: + raise decorators.CommandException(_('Invalid numeric argument for the number of days.')) + + SuspendAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data) + + return decorators.RefreshPageCommand() + +@decorate.withfn(decorators.command) +def report_user(request, id): + user = get_object_or_404(User, id=id) + + if not request.POST.get('publicmsg', None): + return render_to_response('users/report_user.html') + + data = { + 'publicmsg': request.POST.get('publicmsg', _('N/A')), + 'reported': user + } + + ReportAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=data) + + + return decorators.RefreshPageCommand() + + + +def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, render_to=None, weight=500): + def decorator(fn): + def params(request, id=None, slug=None): + # Get the user object by id if the id parameter has been passed + if id is not None: + user = get_object_or_404(User, id=id) + # ...or by slug if the slug has been given + elif slug is not None: + try: + user = User.objects.get(username__iexact=slug) + except User.DoesNotExist: + raise Http404 + + if private and not (user == request.user or request.user.is_superuser): + raise ReturnImediatelyException(HttpResponseUnauthorized(request)) + + if render_to and (not render_to(user)): + raise ReturnImediatelyException(HttpResponseRedirect(user.get_profile_url())) + + return [request, user], { 'slug' : slug, } + + decorated = decorate.params.withfn(params)(fn) + + def result(context_or_response, request, user, **kwargs): + rev_page_title = smart_unicode(user.username) + " - " + tab_description + + # Check whether the return type of the decorated function is a context or Http Response + if isinstance(context_or_response, HttpResponse): + response = context_or_response + + # If it is a response -- show it + return response + else: + # ...if it is a context move forward, update it and render it to response + context = context_or_response + + context.update({ + "tab": "users", + "active_tab" : tab_name, + "tab_description" : tab_description, + "page_title" : rev_page_title, + "can_view_private": (user == request.user) or request.user.is_superuser + }) + return render_to_response(template, context, context_instance=RequestContext(request)) + + decorated = decorate.result.withfn(result, needs_params=True)(decorated) + + if tabbed: + def url_getter(vu): + try: + return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(smart_unicode(vu.username))}) + except NoReverseMatch: + try: + return reverse(fn.__name__, kwargs={'id': vu.id}) + except NoReverseMatch: + return reverse(fn.__name__, kwargs={'slug': slugify(smart_unicode(vu.username))}) + + ui.register(ui.PROFILE_TABS, ui.ProfileTab( + tab_name, tab_title, tab_description,url_getter, private, render_to, weight + )) + + return decorated + return decorator + + +@user_view('users/stats.html', 'stats', _('overview'), _('user overview')) +def user_profile(request, user, **kwargs): + questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at') + answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at') + + # Check whether the passed slug matches the one for the user object + slug = kwargs['slug'] + if slug != slugify(smart_unicode(user.username)): + return HttpResponseRedirect(user.get_absolute_url()) + + up_votes = user.vote_up_count + down_votes = user.vote_down_count + votes_today = user.get_vote_count_today() + votes_total = user.can_vote_count_today() + + user_tags = Tag.objects.filter(Q(nodes__author=user) | Q(nodes__children__author=user)) \ + .annotate(user_tag_usage_count=Count('name')).order_by('-user_tag_usage_count') + + awards = [(Badge.objects.get(id=b['id']), b['count']) for b in + Badge.objects.filter(awards__user=user).values('id').annotate(count=Count('cls')).order_by('-count')] + + return pagination.paginated(request, ( + ('questions', QuestionListPaginatorContext('USER_QUESTION_LIST', _('questions'), default_pagesize=15)), + ('answers', UserAnswersPaginatorContext())), { + "view_user" : user, + "questions" : questions, + "answers" : answers, + "up_votes" : up_votes, + "down_votes" : down_votes, + "total_votes": up_votes + down_votes, + "votes_today_left": votes_total-votes_today, + "votes_total_per_day": votes_total, + "user_tags" : user_tags[:50], + "awards": awards, + "total_awards" : len(awards), + }) + +@user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity')) +def user_recent(request, user, **kwargs): + activities = user.actions.exclude( + action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by( + '-action_date')[:USERS_PAGE_SIZE] + + return {"view_user" : user, "activities" : activities} + + +@user_view('users/reputation.html', 'reputation', _('reputation history'), _('graph of user karma')) +def user_reputation(request, user, **kwargs): + rep = list(user.reputes.order_by('date')) + values = [r.value for r in rep] + redux = lambda x, y: x+y + + graph_data = json.dumps([ + (time.mktime(rep[i].date.timetuple()) * 1000, reduce(redux, values[:i+1], 0)) + for i in range(len(values)) + ]) + + rep = user.reputes.filter(action__canceled=False).order_by('-date')[0:20] + + return {"view_user": user, "reputation": rep, "graph_data": graph_data} + +@user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True) +def user_votes(request, user, **kwargs): + votes = user.votes.exclude(node__state_string__contains="(deleted").filter( + node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE] + + return {"view_user" : user, "votes" : votes} + +@user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite')) +def user_favorites(request, user, **kwargs): + favorites = FavoriteAction.objects.filter(canceled=False, user=user) + + return {"favorites" : favorites, "view_user" : user} + +@user_view('users/subscriptions.html', 'subscriptions', _('subscription'), _('subscriptions'), True, tabbed=False) +def user_subscriptions(request, user, **kwargs): + return _user_subscriptions(request, user, **kwargs) + +def _user_subscriptions(request, user, **kwargs): + enabled = True + + tab = request.GET.get('tab', "settings") + + # Manage tab + if tab == 'manage': + manage_open = True + + auto = request.GET.get('auto', 'True') + if auto == 'True': + show_auto = True + subscriptions = QuestionSubscription.objects.filter(user=user).order_by('-last_view') + else: + show_auto = False + subscriptions = QuestionSubscription.objects.filter(user=user, auto_subscription=False).order_by('-last_view') + + return pagination.paginated(request, ('subscriptions', SubscriptionListPaginatorContext()), { + 'subscriptions':subscriptions, + 'view_user':user, + "auto":show_auto, + 'manage_open':manage_open, + }) + # Settings Tab and everything else + else: + manage_open = False + if request.method == 'POST': + manage_open = False + form = SubscriptionSettingsForm(data=request.POST, instance=user.subscription_settings) + + if form.is_valid(): + form.save() + message = _('New subscription settings are now saved') + + user.subscription_settings.enable_notifications = enabled + user.subscription_settings.save() + + messages.info(request, message) + else: + form = SubscriptionSettingsForm(instance=user.subscription_settings) + + return { + 'view_user':user, + 'notificatons_on': enabled, + 'form':form, + 'manage_open':manage_open, + } + +@user_view('users/preferences.html', 'preferences', _('preferences'), _('preferences'), True, tabbed=False) +def user_preferences(request, user, **kwargs): + if request.POST: + form = UserPreferencesForm(request.POST) + + if form.is_valid(): + user.prop.preferences = form.cleaned_data + messages.info(request, _('New preferences saved')) + + else: + preferences = user.prop.preferences + + if preferences: + form = UserPreferencesForm(initial=preferences) + else: + form = UserPreferencesForm() + + return {'view_user': user, 'form': form} + + diff --git a/forum/views/writers.py b/forum/views/writers.py index 8c7980a..304032c 100644 --- a/forum/views/writers.py +++ b/forum/views/writers.py @@ -12,6 +12,8 @@ from django.http import HttpResponseRedirect, HttpResponse, Http404 from django.utils.html import * from django.utils.translation import ugettext as _ +from django.contrib import messages + from forum.actions import AskAction, AnswerAction, ReviseAction, RollbackAction, RetagAction, AnswerToQuestionAction, CommentToQuestionAction from forum.forms import * from forum.models import * @@ -95,7 +97,7 @@ def ask(request): } if request.user.is_authenticated(): - request.user.message_set.create(message=_("Your question is pending until you %s.") % html.hyperlink( + messages.info(request, _("Your question is pending until you %s.") % html.hyperlink( reverse('send_validation_email'), _("validate your email") )) return HttpResponseRedirect(reverse('index')) @@ -270,7 +272,7 @@ def answer(request, id): } if request.user.is_authenticated(): - request.user.message_set.create(message=_("Your answer is pending until you %s.") % html.hyperlink( + messages.info(request, _("Your answer is pending until you %s.") % html.hyperlink( reverse('send_validation_email'), _("validate your email") )) return HttpResponseRedirect(question.get_absolute_url()) diff --git a/forum_modules/akismet/startup.py b/forum_modules/akismet/startup.py index 2107bbb..2387e05 100644 --- a/forum_modules/akismet/startup.py +++ b/forum_modules/akismet/startup.py @@ -1,7 +1,9 @@ +import json +import loggin + from django.utils.translation import ugettext as _ from django.http import HttpResponse, HttpResponseRedirect from django.template import RequestContext -from django.utils import simplejson from django.utils.encoding import smart_str from django.shortcuts import render_to_response from forum.modules import decorate @@ -15,7 +17,6 @@ from forum.models.user import User from forum.forms.general import SimpleCaptchaForm import settings -import logging def can_bypass_spam_check(user): return user.is_authenticated and (user.is_superuser or user.is_staff or cmp(int(user.reputation), REP_FOR_NO_SPAM_CHECK) > 0) @@ -51,7 +52,7 @@ def check_spam(param, comment_type): 'success': False, 'error_message': _("Sorry, but akismet thinks your %s is spam.") % comment_type } - return HttpResponse(simplejson.dumps(response), mimetype="application/json") + return HttpResponse(json.dumps(response), mimetype="application/json") else: captcha_checked = False try: diff --git a/forum_modules/exporter/templates/running.html b/forum_modules/exporter/templates/running.html index 4dc90ca..9053fcf 100644 --- a/forum_modules/exporter/templates/running.html +++ b/forum_modules/exporter/templates/running.html @@ -94,7 +94,7 @@ if (data.errors == false) { if (exporting) { $('#wait_message').html('{% trans "Your backup is ready to be downloaded."%}'); - $('#download_link_a').attr('href', '{% url exporter_download %}?file=' + data.state.overall.fname) + $('#download_link_a').attr('href', '{% url "exporter_download" %}?file=' + data.state.overall.fname) $('#download_link').slideDown(); } else { $('#wait_message').html('{% trans "All data sucessfully imported."%}') @@ -121,7 +121,7 @@ } }, 1000); - $.getJSON('{% url exporter_state %}', callback); + $.getJSON('{% url "exporter_state" %}', callback); } check_state(); diff --git a/forum_modules/exporter/urls.py b/forum_modules/exporter/urls.py index e9b35aa..1de3851 100644 --- a/forum_modules/exporter/urls.py +++ b/forum_modules/exporter/urls.py @@ -1,5 +1,4 @@ -from django.conf.urls.defaults import * -from django.views.generic.simple import direct_to_template +from django.conf.urls import patterns, url, include from django.utils.translation import ugettext as _ from views import state, running, download diff --git a/forum_modules/exporter/views.py b/forum_modules/exporter/views.py index edbeedc..c7b3904 100644 --- a/forum_modules/exporter/views.py +++ b/forum_modules/exporter/views.py @@ -1,11 +1,11 @@ from __future__ import with_statement import os, tarfile, ConfigParser, datetime +import json from StringIO import StringIO from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.utils.translation import ugettext as _ -from django.utils import simplejson from django.core.cache import cache from django.core.urlresolvers import reverse from forum.views.admin import admin_tools_page, admin_page @@ -74,7 +74,7 @@ def running(request, mode): }) def state(request): - return HttpResponse(simplejson.dumps(cache.get(CACHE_KEY)), mimetype="application/json") + return HttpResponse(json.dumps(cache.get(CACHE_KEY)), mimetype="application/json") @admin_page def download(request): diff --git a/forum_modules/facebookauth/authentication.py b/forum_modules/facebookauth/authentication.py index 8562bad..b80c477 100644 --- a/forum_modules/facebookauth/authentication.py +++ b/forum_modules/facebookauth/authentication.py @@ -12,14 +12,8 @@ from django.utils.translation import ugettext as _ import settings -try: - from json import load as load_json -except Exception: - from django.utils.simplejson import JSONDecoder +from json import load as load_json - def load_json(json): - decoder = JSONDecoder() - return decoder.decode(json.read()) class FacebookAuthConsumer(AuthenticationConsumer): @@ -33,7 +27,7 @@ class FacebookAuthConsumer(AuthenticationConsumer): facebook_api_authentication_url = "https://graph.facebook.com/oauth/authorize?" + urlencode(args) return facebook_api_authentication_url - + def process_authentication_request(self, request): try: args = dict(client_id=settings.FB_API_KEY, redirect_uri="%s%s" % (django_settings.APP_URL, request.path)) diff --git a/forum_modules/facebookauth/templates/button.html b/forum_modules/facebookauth/templates/button.html index 497533d..3b8effa 100644 --- a/forum_modules/facebookauth/templates/button.html +++ b/forum_modules/facebookauth/templates/button.html @@ -1,3 +1,3 @@ -{% load extra_tags %} - - +{% load extra_tags %} + + diff --git a/forum_modules/localauth/templates/loginform.html b/forum_modules/localauth/templates/loginform.html index e66f340..9f088c5 100644 --- a/forum_modules/localauth/templates/loginform.html +++ b/forum_modules/localauth/templates/loginform.html @@ -1,31 +1,31 @@ -{% load i18n %} - -
          -

          {% trans 'Enter your local user name and password' %}
          ({% trans 'or select your external provider above' %})

          - - - - - - - - - - - - - -
          - - - -
          - - - -
          - - - {% trans 'Create account' %} | {% trans 'Forgot your password?' %} -
          -
          +{% load i18n %} + +
          +

          {% trans 'Enter your local user name and password' %}
          ({% trans 'or select your external provider above' %})

          + + + + + + + + + + + + + +
          + + + +
          + + + +
          + + + {% trans 'Create account' %} | {% trans 'Forgot your password?' %} +
          +
          diff --git a/forum_modules/localauth/urls.py b/forum_modules/localauth/urls.py index aeebc40..dfd63b4 100644 --- a/forum_modules/localauth/urls.py +++ b/forum_modules/localauth/urls.py @@ -1,8 +1,7 @@ -from django.conf.urls.defaults import * -from django.views.generic.simple import direct_to_template +from django.conf.urls import patterns, url, include from django.utils.translation import ugettext as _ import views as app urlpatterns = patterns('', url(r'^%s%s%s$' % (_('account/'), _('local/'), _('register/')), app.register, name='auth_local_register'), -) \ No newline at end of file +) diff --git a/forum_modules/oauthauth/authentication.py b/forum_modules/oauthauth/authentication.py index 54a6b4f..d353de4 100644 --- a/forum_modules/oauthauth/authentication.py +++ b/forum_modules/oauthauth/authentication.py @@ -1,11 +1,8 @@ +import json + from consumer import OAuthAbstractAuthConsumer from forum.authentication.base import ConsumerTemplateContext -try: - import json as simplejson -except ImportError: - from django.utils import simplejson - from lib import oauth2 import settings @@ -24,7 +21,7 @@ class TwitterAuthConsumer(OAuthAbstractAuthConsumer): json = self.fetch_data(key, "https://twitter.com/account/verify_credentials.json") if 'screen_name' in json: - creds = simplejson.loads(json) + creds = json.loads(json) return { 'username': creds['screen_name'] @@ -38,4 +35,4 @@ class TwitterAuthContext(ConsumerTemplateContext): type = 'DIRECT' weight = 150 human_name = 'Twitter' - icon = '/media/images/openid/twitter.png' \ No newline at end of file + icon = '/media/images/openid/twitter.png' diff --git a/forum_modules/openidauth/consumer.py b/forum_modules/openidauth/consumer.py index 29a6375..2605587 100644 --- a/forum_modules/openidauth/consumer.py +++ b/forum_modules/openidauth/consumer.py @@ -4,7 +4,6 @@ import re from django.utils.encoding import smart_unicode from django.utils.html import escape -from django.http import get_host from forum.authentication.base import AuthenticationConsumer, InvalidAuthentication import settings @@ -165,8 +164,8 @@ def get_url_host(request): protocol = 'https' else: protocol = 'http' - host = escape(get_host(request)) + host = escape(request.get_host()) return '%s://%s' % (protocol, host) def get_full_url(request): - return get_url_host(request) + request.get_full_path() \ No newline at end of file + return get_url_host(request) + request.get_full_path() diff --git a/forum_modules/openidauth/templates/openidurl.html b/forum_modules/openidauth/templates/openidurl.html index 8659f5a..23d479b 100644 --- a/forum_modules/openidauth/templates/openidurl.html +++ b/forum_modules/openidauth/templates/openidurl.html @@ -1,20 +1,20 @@ -{% load i18n %} -{% load extra_tags %} - -
          - - - - - - - - -

          {% trans 'Enter your OpenId Url' %}

          - - - -
          -
          - +{% load i18n %} +{% load extra_tags %} + +
          + + + + + + + + +

          {% trans 'Enter your OpenId Url' %}

          + + + +
          +
          + diff --git a/forum_modules/robotstxt/urls.py b/forum_modules/robotstxt/urls.py index 0706886..5f74a0a 100644 --- a/forum_modules/robotstxt/urls.py +++ b/forum_modules/robotstxt/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import * +from django.conf.urls import patterns, url, include from django.http import HttpResponse import settings diff --git a/forum_modules/sximporter/templates/page.html b/forum_modules/sximporter/templates/page.html index 42de46d..47fe52c 100644 --- a/forum_modules/sximporter/templates/page.html +++ b/forum_modules/sximporter/templates/page.html @@ -1,28 +1,28 @@ -{% extends "osqaadmin/base.html" %} - -{% load i18n %} -{% load user_tags %} - -{% block subtitle %} - {% trans "SX Importer" %} -{% endblock %} -{% block description %} - {% trans "Welcome to Stack Exchange dump importer." %} -{% endblock %} - -{% block admincontent %} -
          - {% csrf_token %} -
          - {% trans "Your user id in stack exchange" %} -
          - - - {% trans "Merge users with same user name and email" %}
          - - -
          - {% for n in names %} -

          {{ n }}

          - {% endfor %} +{% extends "osqaadmin/base.html" %} + +{% load i18n %} +{% load user_tags %} + +{% block subtitle %} + {% trans "SX Importer" %} +{% endblock %} +{% block description %} + {% trans "Welcome to Stack Exchange dump importer." %} +{% endblock %} + +{% block admincontent %} +
          + {% csrf_token %} +
          + {% trans "Your user id in stack exchange" %} +
          + + + {% trans "Merge users with same user name and email" %}
          + + +
          + {% for n in names %} +

          {{ n }}

          + {% endfor %} {% endblock %} \ No newline at end of file diff --git a/forum_modules/sximporter/urls.py b/forum_modules/sximporter/urls.py index 85fa636..83d887a 100644 --- a/forum_modules/sximporter/urls.py +++ b/forum_modules/sximporter/urls.py @@ -1,9 +1,8 @@ -from django.conf.urls.defaults import * -from django.views.generic.simple import direct_to_template -from django.utils.translation import ugettext as _ - -from views import sximporter - -urlpatterns = patterns('', - url(r'^%s%s$' % (_('admin/'), _('sximporter/')), sximporter, name='sximporter'), -) \ No newline at end of file +from django.conf.urls import patterns, url, include +from django.utils.translation import ugettext as _ + +from views import sximporter + +urlpatterns = patterns('', + url(r'^%s%s$' % (_('admin/'), _('sximporter/')), sximporter, name='sximporter'), +) diff --git a/forum_modules/sximporter/views.py b/forum_modules/sximporter/views.py index ebaed21..fd597b7 100644 --- a/forum_modules/sximporter/views.py +++ b/forum_modules/sximporter/views.py @@ -1,38 +1,38 @@ -from django.shortcuts import render_to_response -from django.template import RequestContext -from forum.http_responses import HttpResponseUnauthorized -from forum.models import User -import importer -from zipfile import ZipFile -import os - -def sximporter(request): - if (not User.objects.exists()) or (request.user.is_authenticated() and request.user.is_superuser): - list = [] - if request.method == "POST" and "dump" in request.FILES: - dump = ZipFile(request.FILES['dump']) - members = [f for f in dump.namelist() if f.endswith('.xml')] - extract_to = os.path.join(os.path.dirname(__file__), 'tmp') - - if not os.path.exists(extract_to): - os.makedirs(extract_to) - - for m in members: - f = open(os.path.join(extract_to, m), 'w') - f.write(dump.read(m)) - f.close() - - #dump.extractall(extract_to, members) - dump.close() - - options = dict([(k, v) for k, v in request.POST.items()]) - options['authenticated_user'] = (request.user.is_authenticated() and (request.user,) or (None,))[0] - - importer.sximport(extract_to, options) - - return render_to_response('modules/sximporter/page.html', { - 'names': list - }, context_instance=RequestContext(request)) - else: - return HttpResponseUnauthorized(request) - +from django.shortcuts import render_to_response +from django.template import RequestContext +from forum.http_responses import HttpResponseUnauthorized +from forum.models import User +import importer +from zipfile import ZipFile +import os + +def sximporter(request): + if (not User.objects.exists()) or (request.user.is_authenticated() and request.user.is_superuser): + list = [] + if request.method == "POST" and "dump" in request.FILES: + dump = ZipFile(request.FILES['dump']) + members = [f for f in dump.namelist() if f.endswith('.xml')] + extract_to = os.path.join(os.path.dirname(__file__), 'tmp') + + if not os.path.exists(extract_to): + os.makedirs(extract_to) + + for m in members: + f = open(os.path.join(extract_to, m), 'w') + f.write(dump.read(m)) + f.close() + + #dump.extractall(extract_to, members) + dump.close() + + options = dict([(k, v) for k, v in request.POST.items()]) + options['authenticated_user'] = (request.user.is_authenticated() and (request.user,) or (None,))[0] + + importer.sximport(extract_to, options) + + return render_to_response('modules/sximporter/page.html', { + 'names': list + }, context_instance=RequestContext(request)) + else: + return HttpResponseUnauthorized(request) + diff --git a/forum_modules/updates/base.py b/forum_modules/updates/base.py index ce5bc8b..fe7a003 100644 --- a/forum_modules/updates/base.py +++ b/forum_modules/updates/base.py @@ -18,9 +18,8 @@ import logging from xml.dom.minidom import parse, parseString from forum.base import get_database_engine from forum.models import Question, Answer, Comment, User -from forum.settings import APP_URL, SVN_REVISION, APP_TITLE, APP_DESCRIPTION +from forum.settings import APP_URL, VCS_REVISION, APP_TITLE, APP_DESCRIPTION from django import VERSION as DJANGO_VERSION -from django.utils import simplejson from django.utils.html import escape from django.utils.encoding import smart_unicode from django.conf import settings as django_settings @@ -82,7 +81,7 @@ def get_admin_emails(): def check_for_updates(): # Get the SVN Revision try: - svn_revision = int(SVN_REVISION.replace('SVN-', '')) + svn_revision = int(VCS_REVISION.replace('SVN-', '')) except ValueError: # Here we'll have to find another way of getting the SVN revision svn_revision = 0 diff --git a/forum_modules/updates/startup.py b/forum_modules/updates/startup.py index f49d106..06d423c 100644 --- a/forum_modules/updates/startup.py +++ b/forum_modules/updates/startup.py @@ -6,7 +6,7 @@ import settings from xml.dom.minidom import parse, parseString from xml.parsers.expat import ExpatError from forum.modules import ui, decorate -from forum.settings import SVN_REVISION +from forum.settings import VCS_REVISION from django.contrib.auth.middleware import AuthenticationMiddleware from django.core.exceptions import ObjectDoesNotExist from django.utils.encoding import smart_str @@ -26,7 +26,7 @@ def process_request(result, self, request): for message in messages: # Get the SVN Revision try: - svn_revision = int(SVN_REVISION.replace('SVN-', '')) + svn_revision = int(VCS_REVISION.replace('SVN-', '')) except ValueError: # Here we'll have to find another way of getting the SVN revision svn_revision = 0 @@ -50,4 +50,4 @@ def process_request(result, self, request): except ExpatError: pass - return result \ No newline at end of file + return result diff --git a/forum_modules/updates/templates/index.html b/forum_modules/updates/templates/index.html index aed0d01..d07d9e3 100644 --- a/forum_modules/updates/templates/index.html +++ b/forum_modules/updates/templates/index.html @@ -43,7 +43,7 @@ $(function() { {% block admincontent %} -{% trans "Check for Updates" %} +{% trans "Check for Updates" %}
          {% endblock %} diff --git a/forum_modules/updates/urls.py b/forum_modules/updates/urls.py index c3a6c4c..58ad7ac 100644 --- a/forum_modules/updates/urls.py +++ b/forum_modules/updates/urls.py @@ -1,5 +1,4 @@ -from django.conf.urls.defaults import * -from django.views.generic.simple import direct_to_template +from django.conf.urls import patterns, url, include from django.utils.translation import ugettext as _ from views import updater_index, updater_check diff --git a/manage.py b/manage.py index f9894f5..f9726f9 100644 --- a/manage.py +++ b/manage.py @@ -1,13 +1,10 @@ #!/usr/bin/env python -from django.core.management import execute_manager -try: - import settings # Assumed to be in the same directory. -except ImportError, e: - import traceback - traceback.print_exc() - import sys - sys.stderr.write("Error: Can't find the file 'forms.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file forms.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) - sys.exit(1) +import os +import sys if __name__ == "__main__": - execute_manager(settings) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/settings.py b/settings.py index cd153f4..1bb2e75 100644 --- a/settings.py +++ b/settings.py @@ -5,15 +5,15 @@ import sys SITE_ID = 1 ADMIN_MEDIA_PREFIX = '/admin_media/' -SECRET_KEY = '$oo^&_m&qwbib=(_4m_n*zn-d=g#s0he5fx9xonnym#8p6yigm' +SECRET_KEY = 'a;::qCl1mfh?avagttOJ;8f5Rr54d,9qy7;o15M2cYO75?OQo51u3LnQ;!8N.:,7' CACHE_MAX_KEY_LENGTH = 235 MIDDLEWARE_CLASSES = [ 'django.middleware.csrf.CsrfViewMiddleware', - 'django.middleware.csrf.CsrfResponseMiddleware', 'forum.middleware.django_cookies.CookiePreHandlerMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.common.CommonMiddleware', 'forum.middleware.extended_user.ExtendedUser', 'forum.middleware.anon_user.ConnectToSessionMessagesMiddleware', @@ -28,8 +28,9 @@ MIDDLEWARE_CLASSES = [ TEMPLATE_CONTEXT_PROCESSORS = [ 'django.core.context_processors.request', 'forum.context.application_settings', + 'django.contrib.messages.context_processors.messages', 'forum.user_messages.context_processors.user_messages', - 'django.core.context_processors.auth', + 'django.contrib.auth.context_processors.auth', ] ROOT_URLCONF = 'urls' @@ -48,25 +49,20 @@ DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' ALLOW_FILE_TYPES = ('.jpg', '.jpeg', '.gif', '.bmp', '.png', '.tiff') ALLOW_MAX_FILE_SIZE = 1024 * 1024 +SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' + +MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage' + # User settings from settings_local import * -if DEBUG: - TEMPLATE_LOADERS = [ - 'django.template.loaders.filesystem.load_template_source', - 'django.template.loaders.app_directories.load_template_source', - 'forum.modules.template_loader.module_templates_loader', - 'forum.skins.load_template_source', - ] -else: - TEMPLATE_LOADERS = [ - ('django.template.loaders.cached.Loader',( - 'django.template.loaders.filesystem.load_template_source', - 'django.template.loaders.app_directories.load_template_source', - 'forum.modules.template_loader.module_templates_loader', - 'forum.skins.load_template_source', - )), - ] +template_loaders = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + 'forum.modules.template_loader.module_templates_loader', + 'forum.skins.load_template_source', +) +TEMPLATE_LOADERS = list(template_loaders) if DEBUG else [ ('django.template.loaders.cached.Loader', template_loaders) ] try: if len(FORUM_SCRIPT_ALIAS) > 0: @@ -117,7 +113,7 @@ INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.humanize', 'django.contrib.sitemaps', - 'django.contrib.markup', + 'django.contrib.messages', 'forum', ] diff --git a/settings_local.py.dist b/settings_local.py.dist index c50bc33..31ad226 100644 --- a/settings_local.py.dist +++ b/settings_local.py.dist @@ -22,7 +22,7 @@ DEBUG_TOOLBAR_CONFIG = { } TEMPLATE_DEBUG = DEBUG INTERNAL_IPS = ('127.0.0.1',) - +ALLOWED_HOSTS = ('yourhostname.com',) DATABASES = { 'default': { diff --git a/urls.py b/urls.py index fb1feda..e56bc01 100644 --- a/urls.py +++ b/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import * +from django.conf.urls import patterns, url, include from django.utils.translation import ugettext as _ from django.conf import settings