-from django.utils.translation import ugettext as _
+from django.utils.translation import ungettext, ugettext as _
from django.db.models import F
from forum.models.action import ActionProxy
from forum.models import Award, Badge, ValidationHash, User
def repute_users(self):
self.repute(self._affected, self._value)
+ self.repute(self.user, -self._value)
- if self._value > 0:
- self._affected.message_set.create(
- message=_("Congratulations, you have been awarded an extra %s reputation points.") % self._value +
- '<br />%s' % self.extra.get('message', _('Thank you')))
- else:
- self._affected.message_set.create(
- message=_("You gave %s reputation points.") % self._value +
- '<br />%s' % self.extra.get('message', ''))
+
+ self._affected.message_set.create(
+ message=_("Congratulations, you have been awarded an extra %(points)s reputation %(points_label)s on <a href=\"%(answer_url)s\">this</a> answer.") % {
+ 'points': self._value,
+ 'points_label': ungettext('point', 'points', self._value),
+ 'answer_url': self.node.get_absolute_url()
+ })
def describe(self, viewer=None):
value = self.extra.get('value', _('unknown'))
if not dic.get('abstract', False):
if not name in installed:
- badge.ondb = Badge(cls=name, type=dic.get('type', Badge.BRONZE))
- badge.ondb.save()
+ ondb = Badge(cls=name, type=dic.get('type', Badge.BRONZE))
+ ondb.save()
else:
- badge.ondb = installed[name]
+ ondb = installed[name]
+
+ badge.ondb = ondb.id
inst = badge()
for action in badge.listen_to:
action.hook(hook)
- BadgesMeta.by_class[name] = badge
- badge.ondb.__dict__['_class'] = inst
- BadgesMeta.by_id[badge.ondb.id] = badge
+ BadgesMeta.by_class[name] = inst
+ BadgesMeta.by_id[ondb.id] = inst
return badge
@classmethod
def award(cls, user, action, once=False):
+ db_object = Badge.objects.get(id=cls.ondb)
try:
if once:
node = None
- awarded = AwardAction.get_for(user, cls.ondb)
+ awarded = AwardAction.get_for(user, db_object)
else:
node = action.node
- awarded = AwardAction.get_for(user, cls.ondb, node)
+ awarded = AwardAction.get_for(user, db_object, node)
trigger = isinstance(action, Action) and action or None
if not awarded:
- AwardAction(user=user, node=node).save(data=dict(badge=cls.ondb, trigger=trigger))
+ AwardAction(user=user, node=node).save(data=dict(badge=db_object, trigger=trigger))
except MultipleObjectsReturned:
if node:
logging.error('Found multiple %s badges awarded for user %s (%s)' % (self.name, user.username, user.id))
--- /dev/null
+import markdown
+import re, socket
+
+TLDS = ('gw', 'gu', 'gt', 'gs', 'gr', 'gq', 'gp', 'gy', 'gg', 'gf', 'ge', 'gd', 'ga', 'edu', 'va', 'gn', 'gl', 'gi',
+ 'gh', 'iq', 'lb', 'lc', 'la', 'tv', 'tw', 'tt', 'arpa', 'lk', 'li', 'lv', 'to', 'lt', 'lr', 'ls', 'th', 'tf',
+ 'su', 'td', 'aspx', 'tc', 'ly', 'do', 'coop', 'dj', 'dk', 'de', 'vc', 'me', 'dz', 'uy', 'yu', 'vg', 'ro',
+ 'vu', 'qa', 'ml', 'us', 'zm', 'cfm', 'tel', 'ee', 'htm', 'za', 'ec', 'bg', 'uk', 'eu', 'et', 'zw',
+ 'es', 'er', 'ru', 'rw', 'rs', 'asia', 're', 'it', 'net', 'gov', 'tz', 'bd', 'be', 'bf', 'asp', 'jobs', 'ba',
+ 'bb', 'bm', 'bn', 'bo', 'bh', 'bi', 'bj', 'bt', 'jm', 'sb', 'bw', 'ws', 'br', 'bs', 'je', 'tg', 'by', 'bz',
+ 'tn', 'om', 'ua', 'jo', 'pdf', 'mz', 'com', 'ck', 'ci', 'ch', 'co', 'cn', 'cm', 'cl', 'cc', 'tr', 'ca', 'cg',
+ 'cf', 'cd', 'cz', 'cy', 'cx', 'org', 'cr', 'txt', 'cv', 'cu', 've', 'pr', 'ps', 'fk', 'pw', 'pt', 'museum',
+ 'py', 'tl', 'int', 'pa', 'pf', 'pg', 'pe', 'pk', 'ph', 'pn', 'eg', 'pl', 'tk', 'hr', 'aero', 'ht', 'hu', 'hk',
+ 'hn', 'vn', 'hm', 'jp', 'info', 'md', 'mg', 'ma', 'mc', 'uz', 'mm', 'local', 'mo', 'mn', 'mh', 'mk', 'cat',
+ 'mu', 'mt', 'mw', 'mv', 'mq', 'ms', 'mr', 'im', 'ug', 'my', 'mx', 'il', 'pro', 'ac', 'sa', 'ae', 'ad', 'ag',
+ 'af', 'ai', 'vi', 'is', 'ir', 'am', 'al', 'ao', 'an', 'aq', 'as', 'ar', 'au', 'at', 'aw', 'in', 'ax', 'az',
+ 'ie', 'id', 'sr', 'nl', 'mil', 'no', 'na', 'travel', 'nc', 'ne', 'nf', 'ng', 'nz', 'dm', 'np',
+ 'so', 'nr', 'nu', 'fr', 'io', 'ni', 'ye', 'sv', 'jsp', 'kz', 'fi', 'fj', 'php', 'fm', 'fo', 'tj', 'sz', 'sy',
+ 'mobi', 'kg', 'ke', 'doc', 'ki', 'kh', 'kn', 'km', 'st', 'sk', 'kr', 'si', 'kp', 'kw', 'sn', 'sm', 'sl', 'sc',
+ 'biz', 'ky', 'sg', 'se', 'sd')
+
+AUTO_LINK_RE = re.compile(r"""
+ (?P<ws>.?\s*)
+ (?P<url>
+ (?P<format1>
+ ((?P<protocol1>[a-z][a-z]+)://)?
+ (?P<domain1>\w(?:[\w-]*\w)?\.\w(?:[\w-]*\w)?(?:\.\w(?:[\w-]*\w)?)*)
+ ) | (?P<format2>
+ ((?P<protocol2>[a-z][a-z]+)://)
+ (?P<domain2>\w(?:[\w-]*\w)?(?:\.\w(?:[\w-]*\w)?)*)
+ )
+ (?P<port>:\d+)?
+ (?P<uri>/[^\s<]*)?
+ )
+
+""", re.X | re.I)
+
+def is_ip(addr):
+ try:
+ socket.inet_aton(addr)
+ return True
+ except:
+ return False
+
+def replacer(m):
+
+ ws = m.group('ws')
+
+ if ws and ws[0] in ("'", '"'):
+ return m.group(0)
+
+ elif not ws:
+ ws = ''
+
+ if m.group('format1'):
+ fn = 1
+ else:
+ fn = 2
+
+ protocol = m.group('protocol%s' % fn)
+ domain = m.group('domain%s' % fn)
+
+ if not protocol:
+ domain_chunks = domain.split('.')
+
+ if not ((len(domain_chunks) == 1 and domain_chunks[0].lower() == 'localhost') or (domain_chunks[-1].lower() in TLDS)):
+ return m.group(0)
+
+ if (not protocol) and is_ip(domain):
+ return m.group(0)
+
+
+ port = m.group('port')
+ uri = m.group('uri')
+
+ if not ws:
+ ws = ''
+
+ if not port:
+ port = ''
+
+ if not protocol:
+ protocol = 'http'
+
+ if not uri:
+ uri = ''
+
+ url = "%s://%s%s%s" % (protocol, domain, port, uri)
+
+ return "%s<a href=\"%s\">%s</a>" % (ws, url, m.group('url'))
+
+
+class AutoLinker(markdown.postprocessors.Postprocessor):
+
+ def run(self, text):
+ return AUTO_LINK_RE.sub(replacer, text)
+
+class AutoLinkerExtension(markdown.Extension):
+
+ def extendMarkdown(self, md, md_globals):
+ md.postprocessors['autolinker'] = AutoLinker()
+
+def makeExtension(configs=None):
+ return AutoLinkerExtension(configs=configs)
+
+
+++ /dev/null
-import markdown
-
-# Global Vars
-URLIZE_RE = '(%s)' % '|'.join([
- r'<(?:f|ht)tps?://[^>]*>',
- r'\b(?:f|ht)tps?://[^)<>\s]+[^.,)<>\s]',
- r'\bwww\.[^)<>\s]+[^.,)<>\s]',
- r'[^*(<\s]+\.(?:com|net|org)\b',
-])
-
-class UrlizePattern(markdown.inlinepatterns.Pattern):
- """ Return a link Element given an autolink (`http://example/com`). """
- def handleMatch(self, m):
- url = m.group(2)
-
- if url.startswith('<'):
- url = url[1:-1]
-
- text = url
-
- if not url.split('://')[0] in ('http','https','ftp'):
- if '@' in url and not '/' in url:
- url = 'mailto:' + url
- else:
- url = 'http://' + url
-
- el = markdown.etree.Element("a")
- el.set('href', url)
- el.text = markdown.AtomicString(text)
- return el
-
-class UrlizeExtension(markdown.Extension):
- """ Urlize Extension for Python-Markdown. """
-
- def extendMarkdown(self, md, md_globals):
- """ Replace autolink with UrlizePattern """
- md.inlinePatterns['autolink'] = UrlizePattern(URLIZE_RE, md)
-
-def makeExtension(configs=None):
- return UrlizeExtension(configs=configs)
-
-if __name__ == "__main__":
- import doctest
- doctest.testmod()
return super(ActionQuerySet, self).obj_from_datadict(datadict)
def get(self, *args, **kwargs):
- action = super(ActionQuerySet, self).get(*args, **kwargs).leaf()
+ action = super(ActionQuerySet, self).get(*args, **kwargs).leaf
if not isinstance(action, self.model):
raise self.model.DoesNotExist()
cancel = ActionRepute(action=self, user=repute.user, value=(-repute.value), by_canceled=True)
cancel.save()
+ @property
def leaf(self):
leaf_cls = ActionProxyMetaClass.types.get(self.action_type, None)
import datetime
import re
+try:
+ from hashlib import md5
+except:
+ from md5 import new as md5
from urllib import quote_plus, urlencode
from django.db import models, IntegrityError, connection, transaction
from django.utils.http import urlquote as django_urlquote
import logging
+if not hasattr(cache, 'get_many'):
+ #put django 1.2 code here
+ pass
+
class LazyQueryList(object):
def __init__(self, model, items):
self.items = items
def __len__(self):
return len(self.items)
+class ToFetch(str):
+ pass
+
class CachedQuerySet(models.query.QuerySet):
def lazy(self):
return LazyQueryList(self.model, list(self.values_list(*values_list)))
else:
- if len(self.query.extra):
- print self.query.extra
return self
def obj_from_datadict(self, datadict):
obj = self.model()
obj.__dict__.update(datadict)
+
+ if hasattr(obj, '_state'):
+ obj._state.db = 'default'
+
return obj
+ def _base_clone(self):
+ return self._clone(klass=models.query.QuerySet)
+
def get(self, *args, **kwargs):
key = self.model.infer_cache_key(kwargs)
obj = cache.get(key)
if obj is None:
- obj = super(CachedQuerySet, self).get(*args, **kwargs)
+ obj = self._base_clone().get(*args, **kwargs)
obj.cache()
else:
obj = self.obj_from_datadict(obj)
return obj
- return super(CachedQuerySet, self).get(*args, **kwargs)
+ return self._base_clone().get(*args, **kwargs)
+
+ def _fetch_from_query_cache(self, key):
+ invalidation_key = self.model._get_cache_query_invalidation_key()
+ cached_result = cache.get_many([invalidation_key, key])
+
+ if not invalidation_key in cached_result:
+ self.model._set_query_cache_invalidation_timestamp()
+ return None
+
+ if (key in cached_result) and(cached_result[invalidation_key] < cached_result[key][0]):
+ return cached_result[key][1]
+
+ return None
+
+ def count(self):
+ cache_key = self.model._generate_cache_key("CNT:%s" % self._get_query_hash())
+ result = self._fetch_from_query_cache(cache_key)
+
+ if result is not None:
+ return result
+
+ result = super(CachedQuerySet, self).count()
+ cache.set(cache_key, (datetime.datetime.now(), result), 60 * 60)
+ return result
+
+ def iterator(self):
+ cache_key = self.model._generate_cache_key("QUERY:%s" % self._get_query_hash())
+ on_cache_query_attr = self.model.value_to_list_on_cache_query()
+
+ to_return = None
+ to_cache = {}
+
+ key_list = self._fetch_from_query_cache(cache_key)
+
+ if key_list is None:
+ if not len(self.query.aggregates):
+ values_list = [on_cache_query_attr]
+
+ if len(self.query.extra):
+ values_list += self.query.extra.keys()
+
+ key_list = [v[0] for v in self.values_list(*values_list)]
+ to_cache[cache_key] = (datetime.datetime.now(), key_list)
+ else:
+ to_return = list(super(CachedQuerySet, self).iterator())
+ to_cache[cache_key] = (datetime.datetime.now(), [row.__dict__[on_cache_query_attr] for row in to_return])
+
+ if (not to_return) and key_list:
+ row_keys = [self.model.infer_cache_key({on_cache_query_attr: attr}) for attr in key_list]
+ cached = cache.get_many(row_keys)
+
+ to_return = [
+ (ck in cached) and self.obj_from_datadict(cached[ck]) or ToFetch(key_list[i]) for i, ck in enumerate(row_keys)
+ ]
+
+ if len(cached) != len(row_keys):
+ to_fetch = [str(tr) for tr in to_return if isinstance(tr, ToFetch)]
+
+ fetched = dict([(str(r.__dict__[on_cache_query_attr]), r) for r in
+ models.query.QuerySet(self.model).filter(**{"%s__in" % on_cache_query_attr: to_fetch})])
+
+ to_return = [(isinstance(tr, ToFetch) and fetched[str(tr)] or tr) for tr in to_return]
+ to_cache.update(dict([(self.model.infer_cache_key({on_cache_query_attr: attr}), r._as_dict()) for attr, r in fetched.items()]))
+
+ if len(to_cache):
+ cache.set_many(to_cache, 60 * 60)
+
+ if to_return:
+ for row in to_return:
+ if hasattr(row, 'leaf'):
+ yield row.leaf
+ else:
+ yield row
+
+ def _get_query_hash(self):
+ return md5(str(self.query)).hexdigest()
+
+
class CachedManager(models.Manager):
use_for_related_fields = True
self.uncache()
self.reset_original_state()
+ self._set_query_cache_invalidation_timestamp()
self.cache()
+ @classmethod
+ def _get_cache_query_invalidation_key(cls):
+ return cls._generate_cache_key("INV_TS")
+
+ @classmethod
+ def _set_query_cache_invalidation_timestamp(cls):
+ cache.set(cls._get_cache_query_invalidation_key(), datetime.datetime.now(), 60 * 60 * 24)
+
+ for base in filter(lambda c: issubclass(c, BaseModel) and (not c is BaseModel), cls.__bases__):
+ base._set_query_cache_invalidation_timestamp()
+
@classmethod
def _generate_cache_key(cls, key, group=None):
if group is None:
def cache_key(self):
return self._generate_cache_key(self.id)
+ @classmethod
+ def value_to_list_on_cache_query(cls):
+ return 'id'
+
@classmethod
def infer_cache_key(cls, querydict):
try:
def delete(self):
self.uncache()
+ self._set_query_cache_invalidation_timestamp()
super(BaseModel, self).delete()
app_label = 'forum'
unique_together = ('user', 'node')
-class BadgesQuerySet(models.query.QuerySet):
- def get(self, *args, **kwargs):
- try:
- pk = [v for (k,v) in kwargs.items() if k in ('pk', 'pk__exact', 'id', 'id__exact')][0]
- except:
- return super(BadgesQuerySet, self).get(*args, **kwargs)
- from forum.badges.base import BadgesMeta
- badge = BadgesMeta.by_id.get(int(pk), None)
- if not badge:
- return super(BadgesQuerySet, self).get(*args, **kwargs)
- return badge.ondb
-
-
-class BadgeManager(models.Manager):
- use_for_related_fields = True
-
- def get_query_set(self):
- return BadgesQuerySet(self.model)
-
-class Badge(models.Model):
+class Badge(BaseModel):
GOLD = 1
SILVER = 2
BRONZE = 3
awarded_to = models.ManyToManyField(User, through='Award', related_name='badges')
- objects = BadgeManager()
+ def get_handler(self):
+ from forum.badges import BadgesMeta
+ return BadgesMeta.by_id.get(self.id, None)
@property
def name(self):
- cls = self.__dict__.get('_class', None)
+ cls = self.get_handler()
return cls and cls.name or _("Unknown")
@property
def description(self):
- cls = self.__dict__.get('_class', None)
+ cls = self.get_handler()
return cls and cls.description or _("No description available")
@models.permalink
from django.utils.safestring import mark_safe
from django.utils.html import strip_tags
from forum.utils.html import sanitize_html
+from forum.utils.userlinking import auto_user_link
from forum.settings import SUMMARY_LENGTH
from utils import PickledObjectField
def html(self):
return self.body
+ def rendered(self, content):
+ return auto_user_link(self, self._as_markdown(content, *['auto_linker']))
+
@classmethod
def _as_markdown(cls, content, *extensions):
try:
def tagname_list(self):
if self.tagnames:
- t = [name.strip() for name in self.tagnames.split(u' ') if name]
return [name.strip() for name in self.tagnames.split(u' ') if name]
else:
return []
def create_revision(self, user, **kwargs):
number = self.revisions.aggregate(last=models.Max('revision'))['last'] + 1
revision = self._create_revision(user, number, **kwargs)
- self.activate_revision(user, revision, extensions=['urlize'])
+ self.activate_revision(user, revision)
return revision
- def activate_revision(self, user, revision, extensions=['urlize']):
+ def activate_revision(self, user, revision):
self.title = revision.title
self.tagnames = revision.tagnames
-
- from forum.utils.userlinking import auto_user_link
-
- self.body = auto_user_link(self, self._as_markdown(revision.body, *extensions))
+
+ self.body = self.rendered(revision.body)
self.active_revision = revision
self.update_last_activity(user)
self.save()
+ def get_active_users(self, active_users = None):
+ if not active_users:
+ active_users = set()
+
+ active_users.add(self.author)
+
+ for node in self.children.all():
+ if not node.nis.deleted:
+ node.get_active_users(active_users)
+
+ return active_users
+
def _list_changes_in_tags(self):
dirty = self.get_dirty_fields()
for name in tag_changes['added']:
try:
tag = Tag.objects.get(name=name)
- except:
+ except Tag.DoesNotExist:
tag = Tag.objects.create(name=name, created_by=self._last_active_user())
if not self.nis.deleted:
tags_changed = self._process_changes_in_tags()
super(Node, self).save(*args, **kwargs)
-
if tags_changed: self.tags = list(Tag.objects.filter(name__in=self.tagname_list()))
class Meta:
return [Question.objects.get(id=r['id']) for r in related_list]
- def get_active_users(self):
- active_users = set()
-
- active_users.add(self.author)
-
- for answer in self.answers:
- active_users.add(answer.author)
-
- for comment in answer.comments:
- active_users.add(comment.author)
-
- for comment in self.comments:
- active_users.add(comment.author)
-
- return active_users
+
class QuestionSubscription(models.Model):
from forum import modules
-class ActiveTagManager(models.Manager):
+class ActiveTagManager(CachedManager):
use_for_related_fields = True
def get_query_set(self):
else:
self.used_count = models.F('used_count') + value
+ def cache_key(self):
+ return self._generate_cache_key(self.name)
+
+ @classmethod
+ def infer_cache_key(cls, querydict):
+ if 'name' in querydict:
+ return cls._generate_cache_key(querydict['name'])
+
+ return BaseModel.infer_cache_key(querydict)
+
+ @classmethod
+ def value_to_list_on_cache_query(cls):
+ return 'name'
+
@models.permalink
def get_absolute_url(self):
return ('tag_questions', (), {'tag': self.name})
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User as DjangoUser, AnonymousUser as DjangoAnonymousUser
from django.db.models import Q
-try:
- from hashlib import md5
-except:
- from md5 import new as md5
import string
from random import Random
except:
return None
+ @classmethod
+ def value_to_list_on_cache_query(cls):
+ return 'key'
+
self.append(item)
+ def find_by_name(self, name):
+ for i in self:
+ if i.name and (i.name == name):
+ return i
+
+ def remove_by_name(self, name):
+ for i, r in enumerate(self):
+ if r.name and (r.name == name):
+ return self.pop(i)
+
HEAD_CONTENT = 'HEAD_CONTENT'
HEADER_LINKS = 'HEADER_LINKS'
from copy import copy
class Visibility(object):
- def __init__(self, level='public'):
+ def __init__(self, level='public', negated=False):
if level not in ['public', 'authenticated', 'staff', 'superuser', 'owner']:
try:
int(level)
self.by_reputation = False
self.level = level
- self.negated = False
+ self.negated = negated
def show_to(self, user):
if self.by_reputation:
return res
def __invert__(self):
- inverted = copy(self)
- inverted.negated = True
+ return Visibility(self.level, not self.negated)
Visibility.PUBLIC = Visibility('public')
else:
return self.argument
- def __init__(self, visibility=None, weight=500):
+ def __init__(self, visibility=None, weight=500, name=''):
self.visibility = visibility
self.weight = weight
+ self.name = name
def _visible_to(self, user):
return (not self.visibility) or (self.visibility and self.visibility.show_to(user))
class Link(ObjectBase):
- def __init__(self, text, url, attrs=None, pre_code='', post_code='', visibility=None, weight=500):
- super(Link, self).__init__(visibility, weight)
+ def __init__(self, text, url, attrs=None, pre_code='', post_code='', visibility=None, weight=500, name=''):
+ super(Link, self).__init__(visibility, weight, name)
self.text = self.Argument(text)
self.url = self.Argument(url)
self.attrs = self.Argument(attrs or {})
self.post_code(context))
class Include(ObjectBase):
- def __init__(self, tpl, visibility=None, weight=500):
- super(Include, self).__init__(visibility, weight)
+ def __init__(self, tpl, visibility=None, weight=500, name=''):
+ super(Include, self).__init__(visibility, weight, name)
self.template = template.loader.get_template(tpl)
def render(self, context):
class LoopContext(LoopBase):
- def __init__(self, loop_context, visibility=None, weight=500):
- super(LoopContext, self).__init__(visibility, weight)
+ def __init__(self, loop_context, visibility=None, weight=500, name=''):
+ super(LoopContext, self).__init__(visibility, weight, name)
self.loop_context = self.Argument(loop_context)
def update_context(self, context):
class PageTab(LoopBase):
- def __init__(self, tab_name, tab_title, url_getter, weight):
- super(PageTab, self).__init__(weight=weight)
+ def __init__(self, tab_name, tab_title, url_getter, weight, name=''):
+ super(PageTab, self).__init__(weight=weight, name=name)
self.tab_name = tab_name
self.tab_title = tab_title
self.url_getter = url_getter
class ProfileTab(LoopBase):
def __init__(self, name, title, description, url_getter, private=False, render_to=None, weight=500):
- super(ProfileTab, self).__init__(weight=weight)
+ super(ProfileTab, self).__init__(weight=weight, name=name)
self.name = name
self.title = title
self.description = description
class AjaxMenuItem(ObjectBase):
- def __init__(self, label, url, a_attrs=None, span_label='', span_attrs=None, visibility=None, weight=500):
- super(AjaxMenuItem, self).__init__(visibility, weight)
+ def __init__(self, label, url, a_attrs=None, span_label='', span_attrs=None, visibility=None, weight=500, name=''):
+ super(AjaxMenuItem, self).__init__(visibility, weight, name)
self.label = self.Argument(label)
self.url = self.Argument(url)
self.a_attrs = self.Argument(a_attrs or {})
**{'class': 'item'})
class AjaxMenuGroup(ObjectBase, Registry):
- def __init__(self, label, items, visibility=None, weight=500):
- super(AjaxMenuGroup, self).__init__(visibility, weight)
+ def __init__(self, label, items, visibility=None, weight=500, name=''):
+ super(AjaxMenuGroup, self).__init__(visibility, weight, name)
self.label = label
for item in items:
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from django.template.defaultfilters import slugify
-from django.template import get_templatetags_modules
+
from forum.templatetags.extra_tags import get_score_badge
from forum.utils.html import cleanup_urls
from forum import settings
-modules_template_tags = get_modules_script('templatetags')
-django_template_tags = get_templatetags_modules()
+try:
+ from django.template import get_templatetags_modules
+ modules_template_tags = get_modules_script('templatetags')
+ django_template_tags = get_templatetags_modules()
-for m in modules_template_tags:
- django_template_tags.append(m.__name__)
+ for m in modules_template_tags:
+ django_template_tags.append(m.__name__)
+except:
+ pass
ui.register(ui.HEADER_LINKS,
- ui.Link(_('faq'), ui.Url('faq'), weight=400),
- ui.Link(_('about'), ui.Url('about'), weight=300),
+ ui.Link(_('faq'), ui.Url('faq'), weight=400, name='FAQ'),
+ ui.Link(_('about'), ui.Url('about'), weight=300, name='ABOUT'),
ui.Link(
text=lambda u, c: u.is_authenticated() and _('logout') or _('login'),
url=lambda u, c: u.is_authenticated() and reverse('logout') or reverse('auth_signin'),
- weight=200),
+ weight=200, name='LOGIN/OUT'),
ui.Link(
visibility=ui.Visibility.AUTHENTICATED,
text=lambda u, c: u.username,
url=lambda u, c: u.get_profile_url(),
post_code=lambda u, c: get_score_badge(u),
- weight=100),
+ weight=100, name='ACCOUNT'),
ui.Link(
visibility=ui.Visibility.SUPERUSER,
text=_('administration'),
url=lambda u, c: reverse('admin_index'),
- weight=0)
+ weight=0, name='ADMINISTRATION')
)
label=_("edit profile"),
url=lambda u, c: reverse('edit_user', kwargs={'id': c['user'].id}),
span_attrs={'class': 'user-edit'},
- weight=0
+ weight=0,
+ name='EDIT_PROFILE'
),
ui.UserMenuItem(
label=_("authentication settings"),
url=lambda u, c: reverse('user_authsettings', kwargs={'id': c['user'].id}),
span_attrs={'class': 'user-auth'},
- weight=100
+ weight=100,
+ name='AUTH_SETTINGS'
),
ui.UserMenuItem(
label=_("email notification settings"),
url=lambda u, c: reverse('user_subscriptions', kwargs={'id': c['user'].id, 'slug': slugify(c['user'].username)}),
span_attrs={'class': 'user-subscriptions'},
- weight=200
+ weight=200,
+ name='EMAIL_SETTINGS'
),
ui.UserMenuItem(
label=_("other preferences"),
url=lambda u, c: reverse('user_preferences', kwargs={'id': c['user'].id, 'slug': slugify(c['user'].username)}),
- weight=200
+ weight=200,
+ name='OTHER_PREFS'
),
ModerationMenuGroup(_("Moderation tools"), items=(
ui.UserMenuItem(
url=lambda u, c: reverse('user_suspend', kwargs={'id': c['user'].id}),
a_attrs=lambda u, c: {'class': c['user'].is_suspended() and 'ajax-command confirm' or 'ajax-command withprompt'},
render_to=lambda u: not u.is_superuser,
+ name='SUSPENSION'
),
ui.UserMenuItem(
label=lambda u, c: _("give/take karma"),
a_attrs=lambda u, c: {'id': 'award-rep-points', 'class': 'ajax-command withprompt'},
span_attrs={'class': 'user-award_rep'},
render_to=lambda u: not u.is_suspended(),
+ name='KARMA'
),
ui.UserMenuItem(
label=lambda u, c: c['user'].is_staff and _("remove moderator status") or _("grant moderator status"),
url=lambda u, c: reverse('user_powers', kwargs={'id': c['user'].id, 'action':c['user'].is_staff and 'remove' or 'grant', 'status': 'staff'}),
a_attrs=lambda u, c: {'class': 'ajax-command confirm'},
span_attrs={'class': 'user-moderator'},
+ name='MODERATOR'
),
SuperUserSwitchMenuItem(
label=lambda u, c: c['user'].is_superuser and _("remove super user status") or _("grant super user status"),
url=lambda u, c: reverse('user_powers', kwargs={'id': c['user'].id, 'action':c['user'].is_superuser and 'remove' or 'grant', 'status': 'super'}),
a_attrs=lambda u, c: {'class': 'ajax-command confirm'},
span_attrs={'class': 'user-superuser'},
+ name='SUPERUSER'
),
- ), visibility=ui.Visibility.SUPERUSER, weight=500)
+ ), visibility=ui.Visibility.SUPERUSER, weight=500, name='MOD_TOOLS')
)
, SIDEBAR_SET, dict(
label = "Question title tips",
help_text = "Tips visible on the ask or edit questions page about the question title.",
-required=False))
+required=False,
+widget=Textarea(attrs={'rows': '10'})))
QUESTION_TAG_TIPS = Setting('QUESTION_TAG_TIPS',
u"""
, SIDEBAR_SET, dict(
label = "Tagging tips",
help_text = "Tips visible on the ask or edit questions page about good tagging.",
-required=False))
+required=False,
+widget=Textarea(attrs={'rows': '10'})))
SIDEBAR_UPPER_SHOW = Setting('SIDEBAR_UPPER_SHOW', True, SIDEBAR_SET, dict(
var $input = $('#id_title');
var $box = $('#ask-related-questions');
var template = $('#question-summary-template').html();
+ var $editor = $('#editor');
var results_cache = {};
$input.keyup(reload_suggestions_box);
$input.focus(reload_suggestions_box);
- $input.blur(close_suggestions_box);
+
+ $editor.change(function() {
+ if ($editor.html().length > 10) {
+ close_suggestions_box();
+ }
+ });
+
+
// for chrome
$input.keydown(focus_on_question);
.user-moderator { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 -51px; }
.user-subscriptions { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 -68px; }
.user-superuser { background: url('/m/default/media/images/user-sprite.png') no-repeat 0 -85px; }
+
{% endif %}
{% endfor %}
{% if has_next %}
- <span class="next"><a href="{{ next_url }}" title="{% trans "next page" %}">{% trans "next page" %} »</a></span>
+ <span class="next"><a href="{{ next_url }}" title="{% trans "next page" %}">{% trans "next" %} »</a></span>
{% endif %}
</p>
{% endspaceless %}
\ No newline at end of file
$('#user-reputation').animate({ backgroundColor: "transparent" }, 1000);
}
</script>
-
<link rel="stylesheet" href="http://jquery-ui.googlecode.com/svn/tags/latest/themes/base/jquery-ui.css" type="text/css" media="all" />
<link rel="stylesheet" href="http://static.jquery.com/ui/css/demo-docs-theme/ui.theme.css" type="text/css" media="all" />
<link rel="stylesheet" type="text/css" media="screen" href="{% media "/media/style/user.css" %}"/>
{% endif %}
+ <style type="text/css">
+ #room {
+ border: 0;
+ }
+ </style>
{% block userjs %}{% endblock %}
{% endblock %}
{% block content %}
{% extends "user.html" %}
-{% load extra_tags %}
-{% load question_list_tags %}
+{% load extra_tags question_list_tags i18n %}
{% block usercontent %}
<div class="user-stats-table">
+{% if favorites %}
{% for favorite in favorites %}
{% question_list_item favorite.node favorite_count=yes signature_type=badges %}
{% endfor %}
+{% else %}
+ {% trans "No favorite questions to display." %}
+{% endif %}
</div>
{% endblock %}
import forum.badges
import forum.subscriptions
import forum.registry
+get_modules_script('registry')
\r
def render(self, context):\r
try:\r
- action = self.activity.resolve(context).leaf()\r
+ action = self.activity.resolve(context).leaf\r
viewer = self.viewer.resolve(context)\r
describe = mark_safe(action.describe(viewer))\r
return self.template.render(template.Context(dict(action=action, describe=describe)))\r
except Exception, e:\r
- #return action.action_type + ":" + str(e)\r
- logging.error("Error in %s action describe: %s" % (action.action_type, str(e)))\r
+ import traceback\r
+ msg = "Error in action describe: \n %s" % (\r
+ traceback.format_exc()\r
+ )\r
+ logging.error(msg)\r
\r
@register.tag\r
def activity_item(parser, token):\r
def get_page():
object_list = page_obj.object_list
- if hasattr(object_list, 'lazy'):
- return object_list.lazy()
+ #if hasattr(object_list, 'lazy'):
+ # return object_list.lazy()
return object_list
paginator.page = get_page()
import re
-from forum.models import User, Question, Answer, Comment
+from forum.models.user import User
def find_best_match_in_name(content, uname, fullname, start_index):
end_index = start_index + len(fullname)
APPEAL_PATTERN = re.compile(r'(?<!\w)@\w+')
def auto_user_link(node, content):
-
- # We should find the root of the node tree (question) the current node belongs to.
- if isinstance(node, Question):
- question = node
- elif isinstance(node, Answer):
- question = node.question
- elif isinstance(node, Comment):
- if node.question:
- question = node.question
- elif node.answer:
- question = node.answer.question
- else:
- return content
-
- # Now we've got the root question. Let's get the list of active users.
- active_users = question.get_active_users()
+
+ active_users = node.absolute_parent.get_active_users()
appeals = APPEAL_PATTERN.finditer(content)
extra = dict(message=request.POST.get('message', ''), awarding_user=request.user.id, value=points)
# We take points from the awarding user
- AwardPointsAction(user=request.user, extra=extra).save(data=dict(value=-points, affected=user))
-
- # And give them to the awarded one
- AwardPointsAction(user=request.user, extra=extra).save(data=dict(value=points, affected=awarded_user))
+ AwardPointsAction(user=request.user, node=answer, extra=extra).save(data=dict(value=points, affected=awarded_user))
return { 'message' : _("You have awarded %s with %d points") % (awarded_user, points) }
@decorators.render('badges.html', 'badges', _('badges'), weight=300)
def badges(request):
- badges = [b.ondb for b in sorted(BadgesMeta.by_id.values(), lambda b1, b2: cmp(b1.name, b2.name))]
+ 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():
my_badges = Award.objects.filter(user=request.user).values('badge_id').distinct()
if content:
try:
tag.text = unicode(content)
- except:
- tag.text = u''
+ except Exception, e:
+ logging.error('error converting unicode characters')
+ import traceback
+ logging.error(traceback.print_exc())
+
+ import string
+ tag.text = unicode("".join([c for c in content if c in string.printable]))
for k, v in attrs.items():
tag.set(k, unicode(v))
--- /dev/null
+NAME = 'Mysql Full Text Search'
+DESCRIPTION = "Enables Mysql full text search functionality."
+
+try:
+ import MySQLdb
+ from django.conf import settings
+ CAN_USE = settings.DATABASE_ENGINE == 'mysql'
+except:
+ CAN_USE = False
+
\ No newline at end of file
--- /dev/null
+CREATE TABLE forum_mysqlftsindex (
+ id int NOT NULL AUTO_INCREMENT,
+ node_id int NOT NULL UNIQUE,
+ body longtext NOT NULL,
+ PRIMARY KEY (id),
+ FOREIGN KEY (node_id) REFERENCES forum_node (id) ON UPDATE CASCADE ON DELETE CASCADE,
+ FULLTEXT (body)
+) ENGINE=`MyISAM`;
+
+ALTER TABLE forum_mysqlftsindex CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+delimiter |
+
+CREATE TRIGGER fts_on_insert AFTER INSERT ON forum_node
+ FOR EACH ROW
+ BEGIN
+ INSERT INTO forum_mysqlftsindex (node_id, body) VALUES (NEW.id, CONCAT_WS('\n', NEW.title, NEW.body, NEW.tagnames));
+ END;
+|
+
+delimiter |
+
+CREATE TRIGGER fts_on_update AFTER UPDATE ON forum_node
+ FOR EACH ROW
+ BEGIN
+ UPDATE forum_mysqlftsindex SET body = CONCAT_WS('\n', NEW.title, NEW.body, NEW.tagnames) WHERE node_id = NEW.id;
+ END;
+
+|
+
+INSERT INTO forum_mysqlftsindex (node_id, body) SELECT id, CONCAT_WS('\n', title, body, tagnames) FROM forum_node;
\ No newline at end of file
--- /dev/null
+from django.db import models
+
+class MysqlFtsIndex(models.Model):
+ node = models.OneToOneField('Node', related_name='ftsindex')
+ body = models.TextField()
+
+ class Meta:
+ managed = False
+ app_label = 'forum'
\ No newline at end of file
--- /dev/null
+from forum.settings.base import Setting
+
+MYSQL_FTS_INSTALLED = Setting('MYSQL_FTS_INSTALLED', False)
\ No newline at end of file
--- /dev/null
+from django.db import connection, transaction
+import os, settings
+
+import re
+from django.db import connection, transaction, models
+from django.db.models import Q
+from forum.models.question import Question, QuestionManager
+from forum.models.node import Node
+from forum.modules import decorate
+
+if not bool(settings.MYSQL_FTS_INSTALLED):
+ f = open(os.path.join(os.path.dirname(__file__), 'fts_install.sql'), 'r')
+
+ try:
+ cursor = connection.cursor()
+ cursor.execute(f.read())
+ transaction.commit_unless_managed()
+
+ settings.MYSQL_FTS_INSTALLED.set_value(True)
+
+ except Exception, e:
+ #import sys, traceback
+ #traceback.print_exc(file=sys.stdout)
+ pass
+ finally:
+ cursor.close()
+
+ f.close()
+
+word_re = re.compile(r'\w+', re.UNICODE)
+
+@decorate(QuestionManager.search, needs_origin=False)
+def question_search(self, keywords):
+ return False, self.filter(models.Q(ftsindex__body__search=keywords))
\ No newline at end of file
for t, s in available_types.items():
if not t in consumer_data:
- consumer_data[t] = axargs["value.%s.1" % s]
+ if axargs.get("value.%s.1" % s, None):
+ consumer_data[t] = axargs["value.%s.1" % s]
request.session['auth_consumer_data'] = consumer_data
post.state_string = "".join("(%s)" % s for s in re.findall('\w+', post.state_string) if s != name)
def postimport(dump, uidmap, tagmap):
- all = []
+ all = {}
def callback(sxpost):
nodetype = (sxpost.get('posttypeid') == '1') and "nodetype" or "answer"
post.extra_count = sxpost.get('viewcount', 0)
add_tags_to_post(post, tagmap)
+ all[int(post.id)] = int(post.id)
else:
post.parent_id = sxpost['parentid']
+ post.abs_parent_id = sxpost['parentid']
+ all[int(post.id)] = int(sxpost['parentid'])
post.save()
- all.append(int(post.id))
create_and_activate_revision(post)
del post
return all
-def comment_import(dump, uidmap, posts):
+def comment_import(dump, uidmap, absparent_map):
+ posts = absparent_map.keys()
+
currid = IdIncrementer(max(posts))
mapping = {}
author_id = uidmap[sxc.get('userid', 1)],
body = sxc['text'],
parent_id = sxc.get('postid'),
+ abs_parent_id = absparent_map.get(int(sxc.get('postid')), sxc.get('postid'))
)
if sxc.get('deletiondate', None):
kv.save()
-def pages_import(dump, currid):
+def pages_import(dump, currid, owner):
currid = IdIncrementer(currid)
registry = {}
'sidebar_render': "html",
'comments': False
}),
- author_id = 1
+ author_id = owner
)
create_and_activate_revision(page)
badges_import(dump, uidmap, posts)
- pages_import(dump, max(posts))
+ pages_import(dump, max(posts), uidmap.default)
static_import(dump)
gc.collect()