summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--forum/const.py3
-rw-r--r--forum/forms.py15
-rw-r--r--forum/management/commands/send_email_alerts.py139
-rw-r--r--forum/managers.py41
-rw-r--r--forum/models.py24
-rw-r--r--forum/templatetags/extra_filters.py5
-rw-r--r--forum/urls.py14
-rw-r--r--forum/views.py1276
-rw-r--r--settings.py4
-rw-r--r--sql_scripts/091208_upgrade_evgeny.sql1
-rw-r--r--sql_scripts/091208_upgrade_evgeny_1.sql1
-rw-r--r--sql_scripts/update_2009_01_25_001.sql2
-rw-r--r--sql_scripts/update_2009_02_26_001.sql2
-rw-r--r--sql_scripts/update_2009_04_10_001.sql2
-rw-r--r--templates/authopenid/complete.html1
-rw-r--r--templates/base.html1
-rw-r--r--templates/content/images/close-small-dark.pngbin0 -> 226 bytes
-rw-r--r--templates/content/js/com.cnprog.admin.js2
-rw-r--r--templates/content/js/com.cnprog.post.js174
-rw-r--r--templates/content/js/com.cnprog.utils.js2
-rw-r--r--templates/content/js/compress.bat3
-rw-r--r--templates/content/js/flot-build.bat2
-rw-r--r--templates/content/style/style.css32
-rw-r--r--templates/index.html18
-rw-r--r--templates/questions.html88
-rw-r--r--templates/tag_selector.html42
-rw-r--r--templates/user_email_subscriptions.html3
-rw-r--r--templates/user_stats.html2
-rw-r--r--utils/decorators.py25
29 files changed, 1192 insertions, 732 deletions
diff --git a/forum/const.py b/forum/const.py
index 312dde26..91b6f5e5 100644
--- a/forum/const.py
+++ b/forum/const.py
@@ -87,3 +87,6 @@ CONST = {
'default_version' : _('initial version'),
'retagged' : _('retagged'),
}
+
+#how to filter questions by tags in email digests?
+TAG_EMAIL_FILTER_CHOICES = (('ignored', _('exclude ignored tags')),('interesting',_('allow only interesting tags')))
diff --git a/forum/forms.py b/forum/forms.py
index 511a0a0d..ecdd9c95 100644
--- a/forum/forms.py
+++ b/forum/forms.py
@@ -232,6 +232,15 @@ class EditUserForm(forms.Form):
raise forms.ValidationError(_('this email has already been registered, please use another one'))
return self.cleaned_data['email']
+class TagFilterSelectionForm(forms.ModelForm):
+ tag_filter_setting = forms.ChoiceField(choices=TAG_EMAIL_FILTER_CHOICES, #imported from forum/const.py
+ initial='ignored',
+ label=_('Choose email tag filter'),
+ widget=forms.RadioSelect)
+ class Meta:
+ model = User
+ fields = ('tag_filter_setting',)
+
class EditUserEmailFeedsForm(forms.Form):
WN = (('w',_('weekly')),('n',_('no email')))
DWN = (('d',_('daily')),('w',_('weekly')),('n',_('no email')))
@@ -247,9 +256,6 @@ class EditUserEmailFeedsForm(forms.Form):
'answered_by_me':'n',
'individually_selected':'n',
}
- all_questions = forms.ChoiceField(choices=DWN,initial='w',
- widget=forms.RadioSelect,
- label=_('Entire forum'),)
asked_by_me = forms.ChoiceField(choices=DWN,initial='w',
widget=forms.RadioSelect,
label=_('Asked by me'))
@@ -259,6 +265,9 @@ class EditUserEmailFeedsForm(forms.Form):
individually_selected = forms.ChoiceField(choices=DWN,initial='w',
widget=forms.RadioSelect,
label=_('Individually selected'))
+ all_questions = forms.ChoiceField(choices=DWN,initial='w',
+ widget=forms.RadioSelect,
+ label=_('Entire forum (tag filtered)'),)
def set_initial_values(self,user=None):
KEY_MAP = dict([(v,k) for k,v in self.FORM_TO_MODEL_MAP.iteritems()])
diff --git a/forum/management/commands/send_email_alerts.py b/forum/management/commands/send_email_alerts.py
index 8d52ed64..563aab69 100644
--- a/forum/management/commands/send_email_alerts.py
+++ b/forum/management/commands/send_email_alerts.py
@@ -38,73 +38,84 @@ class Command(NoArgsCommand):
).exclude(
closed=True
)
- user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude(frequency='n')
- for feed in user_feeds:
- cutoff_time = now - EmailFeedSetting.DELTA_TABLE[feed.frequency]
- if feed.reported_at == None or feed.reported_at <= cutoff_time:
- Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later
- feed.reported_at = now
- feed.save()#may not actually report anything, depending on filters below
- if feed.feed_type == 'q_sel':
- q_sel = Q_set.filter(followed_by=user)
- q_sel.cutoff_time = cutoff_time #store cutoff time per query set
- elif feed.feed_type == 'q_ask':
- q_ask = Q_set.filter(author=user)
- q_ask.cutoff_time = cutoff_time
- elif feed.feed_type == 'q_ans':
- q_ans = Q_set.filter(answers__author=user)
- q_ans.cutoff_time = cutoff_time
- elif feed.feed_type == 'q_all':
- q_all = Q_set
- q_all.cutoff_time = cutoff_time
- #build list in this order
- q_list = OrderedDict()
- def extend_question_list(src, dst):
- """src is a query set with questions
- or an empty list
- dst - is an ordered dictionary
- """
- if src is None:
- return #will not do anything if subscription of this type is not used
- cutoff_time = src.cutoff_time
- for q in src:
- if q in dst:
- if cutoff_time < dst[q]['cutoff_time']:
- dst[q]['cutoff_time'] = cutoff_time
- else:
- #initialise a questions metadata dictionary to use for email reporting
- dst[q] = {'cutoff_time':cutoff_time}
+ #todo: still need to add back individually selected and other questions....
+ #these may be filtered out by tags
+ if user.tag_filter_setting == 'ignored':
+ ignored_tags = user.markedtag_set.filter(reason='bad').values_list('tag', flat=True).distinct()
+ Q_set1 = Q_set1.exclude( tags__in=ignored_tags )
+ logging.debug('removed ignored tags')
+ else:
+ selected_tags = user.markedtag_set.filter(reason='good').values_list('tag', flat=True).distinct()
+ Q_set1 = Q_set1.filter( tags__in=selected_tags )
+ logging.debug('filtered for only selected tags')
+
+ user_feeds = EmailFeedSetting.objects.filter(subscriber=user).exclude(frequency='n')
+ for feed in user_feeds:
+ cutoff_time = now - EmailFeedSetting.DELTA_TABLE[feed.frequency]
+ if feed.reported_at == None or feed.reported_at <= cutoff_time:
+ Q_set = Q_set1.exclude(last_activity_at__gt=cutoff_time)#report these excluded later
+ feed.reported_at = now
+ feed.save()#may not actually report anything, depending on filters below
+ if feed.feed_type == 'q_sel':
+ q_sel = Q_set.filter(followed_by=user)
+ q_sel.cutoff_time = cutoff_time #store cutoff time per query set
+ elif feed.feed_type == 'q_ask':
+ q_ask = Q_set.filter(author=user)
+ q_ask.cutoff_time = cutoff_time
+ elif feed.feed_type == 'q_ans':
+ q_ans = Q_set.filter(answers__author=user)
+ q_ans.cutoff_time = cutoff_time
+ elif feed.feed_type == 'q_all':
+ q_all = Q_set
+ q_all.cutoff_time = cutoff_time
+ #build list in this order
+ q_list = OrderedDict()
+ def extend_question_list(src, dst):
+ """src is a query set with questions
+ or an empty list
+ dst - is an ordered dictionary
+ """
+ if src is None:
+ return #will not do anything if subscription of this type is not used
+ cutoff_time = src.cutoff_time
+ for q in src:
+ if q in dst:
+ if cutoff_time < dst[q]['cutoff_time']:
+ dst[q]['cutoff_time'] = cutoff_time
+ else:
+ #initialise a questions metadata dictionary to use for email reporting
+ dst[q] = {'cutoff_time':cutoff_time}
- extend_question_list(q_sel, q_list)
- extend_question_list(q_ask, q_list)
- extend_question_list(q_ans, q_list)
- extend_question_list(q_all, q_list)
+ extend_question_list(q_sel, q_list)
+ extend_question_list(q_ask, q_list)
+ extend_question_list(q_ans, q_list)
+ extend_question_list(q_all, q_list)
- ctype = ContentType.objects.get_for_model(Question)
- EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT
- for q, meta_data in q_list.items():
- #todo use Activity, but first start keeping more Activity records
- #act = Activity.objects.filter(content_type=ctype, object_id=q.id)
- #because currently activity is not fully recorded to through
- #revision records to see what kind modifications were done on
- #the questions and answers
- try:
- update_info = Activity.objects.get(content_type=ctype,
- object_id=q.id,
- activity_type=EMAIL_UPDATE_ACTIVITY)
- emailed_at = update_info.active_at
- except Activity.DoesNotExist:
- update_info = Activity(user=user, content_object=q, activity_type=EMAIL_UPDATE_ACTIVITY)
- emailed_at = datetime.datetime(1970,1,1)#long time ago
- except Activity.MultipleObjectsReturned:
- raise Exception('server error - multiple question email activities found per user-question pair')
+ ctype = ContentType.objects.get_for_model(Question)
+ EMAIL_UPDATE_ACTIVITY = const.TYPE_ACTIVITY_QUESTION_EMAIL_UPDATE_SENT
+ for q, meta_data in q_list.items():
+ #todo use Activity, but first start keeping more Activity records
+ #act = Activity.objects.filter(content_type=ctype, object_id=q.id)
+ #because currently activity is not fully recorded to through
+ #revision records to see what kind modifications were done on
+ #the questions and answers
+ try:
+ update_info = Activity.objects.get(content_type=ctype,
+ object_id=q.id,
+ activity_type=EMAIL_UPDATE_ACTIVITY)
+ emailed_at = update_info.active_at
+ except Activity.DoesNotExist:
+ update_info = Activity(user=user, content_object=q, activity_type=EMAIL_UPDATE_ACTIVITY)
+ emailed_at = datetime.datetime(1970,1,1)#long time ago
+ except Activity.MultipleObjectsReturned:
+ raise Exception('server error - multiple question email activities found per user-question pair')
- q_rev = QuestionRevision.objects.filter(question=q,\
- revised_at__lt=cutoff_time,\
- revised_at__gt=emailed_at)
- q_rev = q_rev.exclude(author=user)
- meta_data['q_rev'] = len(q_rev)
- if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at:
+ q_rev = QuestionRevision.objects.filter(question=q,\
+ revised_at__lt=cutoff_time,\
+ revised_at__gt=emailed_at)
+ q_rev = q_rev.exclude(author=user)
+ meta_data['q_rev'] = len(q_rev)
+ if len(q_rev) > 0 and q.added_at == q_rev[0].revised_at:
meta_data['q_rev'] = 0
meta_data['new_q'] = True
else:
diff --git a/forum/managers.py b/forum/managers.py
index 56967f64..90437e91 100644
--- a/forum/managers.py
+++ b/forum/managers.py
@@ -7,25 +7,6 @@ from forum.models import *
from urllib import quote, unquote
class QuestionManager(models.Manager):
- def get_translation_questions(self, orderby, page_size):
- questions = self.filter(deleted=False, author__id__in=[28,29]).order_by(orderby)[:page_size]
- return questions
-
- def get_questions_by_pagesize(self, orderby, page_size):
- questions = self.filter(deleted=False).order_by(orderby)[:page_size]
- return questions
-
- def get_questions_by_tag(self, tagname, orderby):
- questions = self.filter(deleted=False, tags__name = unquote(tagname)).order_by(orderby)
- return questions
-
- def get_unanswered_questions(self, orderby):
- questions = self.filter(deleted=False, answer_accepted=False).order_by(orderby)
- return questions
-
- def get_questions(self, orderby):
- questions = self.filter(deleted=False).order_by(orderby)
- return questions
def update_tags(self, question, tagnames, user):
"""
@@ -92,12 +73,11 @@ class QuestionManager(models.Manager):
Questions with the individual tags will be added to list if above questions are not full.
"""
#print datetime.datetime.now()
- from forum.models import Question
- questions = list(Question.objects.filter(tagnames = question.tagnames, deleted=False).all())
+ questions = list(self.filter(tagnames = question.tagnames, deleted=False).all())
tags_list = question.tags.all()
for tag in tags_list:
- extend_questions = Question.objects.filter(tags__id = tag.id, deleted=False)[:50]
+ extend_questions = self.filter(tags__id = tag.id, deleted=False)[:50]
for item in extend_questions:
if item not in questions and len(questions) < 10:
questions.append(item)
@@ -166,22 +146,17 @@ class TagManager(models.Manager):
class AnswerManager(models.Manager):
GET_ANSWERS_FROM_USER_QUESTIONS = u'SELECT answer.* FROM answer INNER JOIN question ON answer.question_id = question.id WHERE question.author_id =%s AND answer.author_id <> %s'
- def get_answers_from_question(self, question, user=None, other_orderby = None):
+ def get_answers_from_question(self, question, user=None):
"""
Retrieves visibile answers for the given question. Delete answers
are only visibile to the person who deleted them.
- """
+ """
+
if user is None or not user.is_authenticated():
- q = self.filter(question=question, deleted=False)
- else:
- q = self.filter(Q(question=question),
- Q(deleted=False) | Q(deleted_by=user))
- if other_orderby is None:
- q = q.order_by("-accepted")
+ return self.filter(question=question, deleted=False)
else:
- q = q.order_by("-accepted", other_orderby)
-
- return q
+ return self.filter(Q(question=question),
+ Q(deleted=False) | Q(deleted_by=user))
def get_answers_from_questions(self, user_id):
"""
diff --git a/forum/models.py b/forum/models.py
index 27f88dcc..8873fd87 100644
--- a/forum/models.py
+++ b/forum/models.py
@@ -267,16 +267,6 @@ class Question(models.Model):
return when, who
- def get_user_votes_in_answers(self, user):
- content_type = ContentType.objects.get_for_model(Answer)
- query_set = Vote.objects.extra(
- tables = ['question', 'answer'],
- where = ['question.id = answer.question_id AND question.id = %s AND vote.object_id = answer.id AND vote.content_type_id = %s AND vote.user_id = %s'],
- params = [self.id, content_type.id, user.id]
- )
-
- return query_set
-
def get_update_summary(self,last_reported_at=None,recipient_email=''):
edited = False
if self.last_edited_at and self.last_edited_at > last_reported_at:
@@ -354,6 +344,12 @@ class FavoriteQuestion(models.Model):
def __unicode__(self):
return '[%s] favorited at %s' %(self.user, self.added_at)
+class MarkedTag(models.Model):
+ TAG_MARK_REASONS = (('good',_('interesting')),('bad',_('ignored')))
+ tag = models.ForeignKey(Tag)
+ user = models.ForeignKey(User)
+ reason = models.CharField(max_length=16, choices=TAG_MARK_REASONS)
+
class QuestionRevision(models.Model):
"""A revision of a Question."""
question = models.ForeignKey(Question, related_name='revisions')
@@ -702,6 +698,14 @@ User.add_to_class('date_of_birth', models.DateField(null=True, blank=True))
User.add_to_class('about', models.TextField(blank=True))
User.add_to_class('is_username_taken',classmethod(user_is_username_taken))
User.add_to_class('get_q_sel_email_feed_frequency',user_get_q_sel_email_feed_frequency)
+User.add_to_class('hide_ignored_questions', models.BooleanField(default=False))
+User.add_to_class('tag_filter_setting',
+ models.CharField(
+ max_length=16,
+ choices=TAG_EMAIL_FILTER_CHOICES,
+ default='ignored'
+ )
+ )
# custom signal
tags_updated = django.dispatch.Signal(providing_args=["question"])
diff --git a/forum/templatetags/extra_filters.py b/forum/templatetags/extra_filters.py
index 3644fdc3..22ec0109 100644
--- a/forum/templatetags/extra_filters.py
+++ b/forum/templatetags/extra_filters.py
@@ -1,4 +1,5 @@
from django import template
+from django.core import serializers
from forum import auth
import logging
@@ -91,3 +92,7 @@ def cnprog_intword(number):
return number
except:
return number
+
+@register.filter
+def json_serialize(object):
+ return serializers.serialize('json',object)
diff --git a/forum/urls.py b/forum/urls.py
index ee4f0992..927e149c 100644
--- a/forum/urls.py
+++ b/forum/urls.py
@@ -39,6 +39,7 @@ urlpatterns = patterns('',
url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')), app.vote, name='vote'),
url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')), app.question_revisions, name='question_revisions'),
url(r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')), app.question_comments, name='question_comments'),
+ url(r'^%s$' % _('command/'), app.ajax_command, name='call_ajax'),
url(r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$' % (_('questions/'), _('comments/'),_('delete/')), \
app.delete_comment, kwargs={'commented_object_type':'question'},\
@@ -51,6 +52,19 @@ urlpatterns = patterns('',
url(r'^%s(?P<id>\d+)//*' % _('question/'), app.question, name='question'),
url(r'^%s$' % _('tags/'), app.tags, name='tags'),
url(r'^%s(?P<tag>[^/]+)/$' % _('tags/'), app.tag, name='tag_questions'),
+
+ url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')), app.mark_tag, \
+ kwargs={'reason':'good','action':'add'}, \
+ name='mark_interesting_tag'),
+
+ url(r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('ignored/')), app.mark_tag, \
+ kwargs={'reason':'bad','action':'add'}, \
+ name='mark_ignored_tag'),
+
+ url(r'^%s(?P<tag>[^/]+)/$' % _('unmark-tag/'), app.mark_tag, \
+ kwargs={'action':'remove'}, \
+ name='mark_ignored_tag'),
+
url(r'^%s$' % _('users/'),app.users, name='users'),
url(r'^%s(?P<id>\d+)/$' % _('moderate-user/'), app.moderate_user, name='moderate_user'),
url(r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')), app.edit_user, name='edit_user'),
diff --git a/forum/views.py b/forum/views.py
index 8ff44f4b..f1eb4d0c 100644
--- a/forum/views.py
+++ b/forum/views.py
@@ -1,33 +1,36 @@
# encoding:utf-8
-import calendar
+import os.path
+import time, datetime, calendar, random
+import logging
+from urllib import quote, unquote
from django.conf import settings
+from django.core.files.storage import default_storage
+from django.shortcuts import render_to_response, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden, Http404
from django.core.paginator import Paginator, EmptyPage, InvalidPage
from django.template import RequestContext, loader
+from django.utils.html import *
+from django.utils import simplejson
+from django.core import serializers
from django.core.mail import mail_admins
+from django.db import transaction
+from django.db.models import Count, Q
from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import ugettext as _
+from django.utils.datastructures import SortedDict
from django.template.defaultfilters import slugify
from django.core.exceptions import PermissionDenied
+
from utils.html import sanitize_html
-from django.core.files.storage import default_storage
-from django.shortcuts import get_object_or_404
-from django.shortcuts import render_to_response
-from django.utils import simplejson
-from django.utils.html import *
-from django.utils.translation import ugettext as _
+from utils.decorators import ajax_method, ajax_login_required
from markdown2 import Markdown
-import os.path
-import random
-import time
-
-import datetime
-from forum import auth
-from forum.auth import *
-from forum.const import *
+#from lxml.html.diff import htmldiff
from forum.diff import textDiff as htmldiff
from forum.forms import *
from forum.models import *
+from forum.auth import *
+from forum.const import *
from forum.user import *
from forum import auth
from django_authopenid.util import get_next_url
@@ -87,19 +90,31 @@ def index(request):
}
view_id, orderby = _get_and_remember_questions_sort_method(request, view_dic, 'latest')
- questions = Question.objects.get_questions_by_pagesize(orderby, INDEX_PAGE_SIZE)
+ page_size = request.session.get('pagesize', QUESTIONS_PAGE_SIZE)
+ questions = Question.objects.exclude(deleted=True).order_by(orderby)[:page_size]
# RISK - inner join queries
questions = questions.select_related()
tags = Tag.objects.get_valid_tags(INDEX_TAGS_SIZE)
awards = Award.objects.get_recent_awards()
+ (interesting_tag_names, ignored_tag_names) = (None, None)
+ if request.user.is_authenticated():
+ pt = MarkedTag.objects.filter(user=request.user)
+ 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)
+
+ tags_autocomplete = _get_tags_cache_json()
+
return render_to_response('index.html', {
- "questions": questions,
- "tab_id": view_id,
- "tags": tags,
- "awards": awards[:INDEX_AWARD_SIZE],
- }, context_instance=RequestContext(request))
+ 'interesting_tag_names': interesting_tag_names,
+ 'tags_autocomplete': tags_autocomplete,
+ 'ignored_tag_names': ignored_tag_names,
+ "questions" : questions,
+ "tab_id" : view_id,
+ "tags" : tags,
+ "awards" : awards[:INDEX_AWARD_SIZE],
+ }, context_instance=RequestContext(request))
def about(request):
return render_to_response('about.html', context_instance=RequestContext(request))
@@ -145,10 +160,12 @@ def questions(request, tagname=None, unanswered=False):
List of Questions, Tagged questions, and Unanswered questions.
"""
# template file
- # "questions.html" or "unanswered.html"
+ # "questions.html" or maybe index.html in the future
template_file = "questions.html"
+ # Set flag to False by default. If it is equal to True, then need to be saved.
+ pagesize_changed = False
# get pagesize from session, if failed then get default value
- pagesize = request.session.get("pagesize", 10)
+ pagesize = request.session.get("pagesize",10)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
@@ -158,68 +175,172 @@ def questions(request, tagname=None, unanswered=False):
view_id, orderby = _get_and_remember_questions_sort_method(request,view_dic,'latest')
# check if request is from tagged questions
+ qs = Question.objects.exclude(deleted=True)
+
if tagname is not None:
- objects = Question.objects.get_questions_by_tag(tagname, orderby)
- elif unanswered:
- #check if request is from unanswered questions
- template_file = "unanswered.html"
- objects = Question.objects.get_unanswered_questions(orderby)
- else:
- objects = Question.objects.get_questions(orderby)
+ qs = qs.filter(tags__name = unquote(tagname))
+
+ if unanswered:
+ qs = qs.exclude(answer_accepted=True)
author_name = None
+ #user contributed questions & answers
if 'user' in request.GET:
try:
author_name = request.GET['user']
u = User.objects.get(username=author_name)
- objects = objects.filter(Q(author=u) | Q(answers__author=u))
+ qs = qs.filter(Q(author=u) | Q(answers__author=u))
except User.DoesNotExist:
author_name = None
- # RISK - inner join queries
- objects = objects.select_related(depth=1);
- objects_list = Paginator(objects, pagesize)
- questions = objects_list.page(page)
+ if request.user.is_authenticated():
+ uid_str = str(request.user.id)
+ qs = qs.extra(
+ select = SortedDict([
+ (
+ 'interesting_score',
+ 'SELECT COUNT(1) FROM forum_markedtag, question_tags '
+ + 'WHERE forum_markedtag.user_id = %s '
+ + 'AND forum_markedtag.tag_id = question_tags.tag_id '
+ + 'AND forum_markedtag.reason = "good" '
+ + 'AND question_tags.question_id = question.id'
+ ),
+ (
+ 'ignored_score',
+ 'SELECT COUNT(1) FROM forum_markedtag, question_tags '
+ + 'WHERE forum_markedtag.user_id = %s '
+ + 'AND forum_markedtag.tag_id = question_tags.tag_id '
+ + 'AND forum_markedtag.reason = "bad" '
+ + 'AND question_tags.question_id = question.id'
+ )
+ ]),
+ select_params = (uid_str, uid_str)
+ )
+ #if request.user.hide_ignored_questions:
+ # qs = qs.extra(where=['ignored_score=0']) #this doesn't work,
+ #no way to filter on ignored_score!
+
+ qs = qs.select_related(depth=1).order_by(orderby)
+
+ #don't know how to get around this - maybe have to use raw sql?
+ #the probjem is that it seems to be impossible to exclude ingored questions
+ #from the query set - 'the django way'
+ #the erzatz paginator below compensates for the current lack of proper SQL statement
+ class DummyQuerySet(list):
+ def __init__(self,items):
+ super(DummyQuerySet, self).__init__(items)
+ def count(self):
+ return len(self)
+
+ class DummyPage(list):
+ def __init__(self,items,num=1,has_next=True):
+ self.object_list = DummyQuerySet(items)
+ self.num = num
+ self.has_next_page = has_next
+ def count(self):
+ return len(self.object_list)
+ def has_next(self):
+ return self.has_next_page
+ def has_previous(self):
+ return (self.num > 1)
+ def previous_page_number(self):
+ if self.has_previous():
+ return self.num - 1
+ return self.num
+ def next_page_number(self):
+ if self.has_next():
+ return self.num + 1
+ return self.num
+
+ class HidingIgnoredPaginator(object):
+ def __init__(self,query_set,page_size):
+ self.query_set = list(query_set)#force db hit
+ self.page_size = page_size
+ self.pages = []
+ self.count = 0
+ cpage = []
+ for q in self.query_set:
+ if self.count % page_size == 0:
+ if len(cpage) > 0:
+ self.pages.append(cpage)
+ cpage = []
+ if q.ignored_score == 0:
+ cpage.append(q)
+ self.count += 1
+ if cpage not in self.pages and len(cpage) > 0:
+ self.pages.append(cpage)
+ self.num_pages = len(self.pages)
+
+ def page(self,num):
+ if self.num_pages == 0:
+ return DummyPage([],num=1,has_next=False)
+ elif num >= self.num_pages:
+ page_content = self.pages[-1]
+ num = self.num_pages
+ has_next = False
+ else:
+ page_content = self.pages[num-1]
+ has_next = True
+ return DummyPage(page_content,num=num,has_next=has_next)
+
+ if request.user.is_authenticated() and request.user.hide_ignored_questions:
+ objects_list = HidingIgnoredPaginator(qs, pagesize)
+ questions = objects_list.page(page)
+ else: #otherwise just use the paginator
+ objects_list = Paginator(qs, pagesize)
+ questions = objects_list.page(page)
# Get related tags from this page objects
if questions.object_list.count() > 0:
related_tags = Tag.objects.get_tags_by_questions(questions.object_list)
else:
related_tags = None
+ tags_autocomplete = _get_tags_cache_json()
+
+ # get the list of interesting and ignored tags
+ (interesting_tag_names, ignored_tag_names) = (None, None)
+ if request.user.is_authenticated():
+ pt = MarkedTag.objects.filter(user=request.user)
+ 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)
+
return render_to_response(template_file, {
- "questions": questions,
- "author_name": author_name,
- "tab_id": view_id,
- "questions_count": objects_list.count,
- "tags": related_tags,
- "searchtag": tagname,
- "is_unanswered": unanswered,
- "context": {
- 'is_paginated': True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': questions.has_previous(),
- 'has_next': questions.has_next(),
- 'previous': questions.previous_page_number(),
- 'next': questions.next_page_number(),
- 'base_url': request.path + '?sort=%s&' % view_id,
- 'pagesize': pagesize
- }}, context_instance=RequestContext(request))
-
-def create_new_answer(question=None, author=None, \
- added_at=None, wiki=False, \
- text='', email_notify=False):
+ "questions" : questions,
+ "author_name" : author_name,
+ "tab_id" : view_id,
+ "questions_count" : objects_list.count,
+ "tags" : related_tags,
+ "tags_autocomplete" : tags_autocomplete,
+ "searchtag" : tagname,
+ "is_unanswered" : unanswered,
+ "interesting_tag_names": interesting_tag_names,
+ 'ignored_tag_names': ignored_tag_names,
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': questions.has_previous(),
+ 'has_next': questions.has_next(),
+ 'previous': questions.previous_page_number(),
+ 'next': questions.next_page_number(),
+ 'base_url' : request.path + '?sort=%s&' % view_id,
+ 'pagesize' : pagesize
+ }}, context_instance=RequestContext(request))
+
+def create_new_answer( question=None, author=None,\
+ added_at=None, wiki=False,\
+ text='', email_notify=False):
html = sanitize_html(markdowner.convert(text))
#create answer
answer = Answer(
- question=question,
- author=author,
- added_at=added_at,
- wiki=wiki,
- html=html
- )
+ question = question,
+ author = author,
+ added_at = added_at,
+ wiki = wiki,
+ html = html
+ )
if answer.wiki:
answer.last_edited_by = answer.author
answer.last_edited_at = added_at
@@ -235,13 +356,13 @@ def create_new_answer(question=None, author=None, \
#update revision
AnswerRevision.objects.create(
- answer=answer,
- revision=1,
- author=author,
- revised_at=added_at,
- summary=CONST['default_version'],
- text=text
- )
+ answer = answer,
+ revision = 1,
+ author = author,
+ revised_at = added_at,
+ summary = CONST['default_version'],
+ text = text
+ )
#set notification/delete
if email_notify:
@@ -254,24 +375,24 @@ def create_new_answer(question=None, author=None, \
except:
pass
-def create_new_question(title=None, author=None, added_at=None,
- wiki=False, tagnames=None, summary=None,
+def create_new_question(title=None,author=None,added_at=None,
+ wiki=False,tagnames=None,summary=None,
text=None):
"""this is not a view
and maybe should become one of the methods on Question object?
"""
html = sanitize_html(markdowner.convert(text))
question = Question(
- title=title,
- author=author,
- added_at=added_at,
- last_activity_at=added_at,
- last_activity_by=author,
- wiki=wiki,
- tagnames=tagnames,
- html=html,
- summary=summary
- )
+ title = title,
+ author = author,
+ added_at = added_at,
+ last_activity_at = added_at,
+ last_activity_by = author,
+ wiki = wiki,
+ tagnames = tagnames,
+ html = html,
+ summary = summary
+ )
if question.wiki:
question.last_edited_by = question.author
question.last_edited_at = added_at
@@ -281,15 +402,15 @@ def create_new_question(title=None, author=None, added_at=None,
# create the first revision
QuestionRevision.objects.create(
- question=question,
- revision=1,
- title=question.title,
- author=author,
- revised_at=added_at,
- tagnames=question.tagnames,
- summary=CONST['default_version'],
- text=text
- )
+ question = question,
+ revision = 1,
+ title = question.title,
+ author = author,
+ revised_at = added_at,
+ tagnames = question.tagnames,
+ summary = CONST['default_version'],
+ text = text
+ )
return question
#TODO: allow anynomus user to ask question by providing email and username.
@@ -300,7 +421,7 @@ def ask(request):
if form.is_valid():
added_at = datetime.datetime.now()
- title = strip_tags(form.cleaned_data['title']).strip()
+ title = strip_tags(form.cleaned_data['title'].strip())
wiki = form.cleaned_data['wiki']
tagnames = form.cleaned_data['tags'].strip()
text = form.cleaned_data['text']
@@ -311,29 +432,29 @@ def ask(request):
author = request.user
question = create_new_question(
- title=title,
- author=author,
- added_at=added_at,
- wiki=wiki,
- tagnames=tagnames,
- summary=summary,
- text=text
- )
+ title = title,
+ author = author,
+ added_at = added_at,
+ wiki = wiki,
+ tagnames = tagnames,
+ summary = summary,
+ text = text
+ )
return HttpResponseRedirect(question.get_absolute_url())
else:
request.session.flush()
session_key = request.session.session_key
question = AnonymousQuestion(
- session_key=session_key,
- title=title,
- tagnames=tagnames,
- wiki=wiki,
- text=text,
- summary=summary,
- added_at=added_at,
- ip_addr=request.META['REMOTE_ADDR'],
- )
+ session_key = session_key,
+ title = title,
+ tagnames = tagnames,
+ wiki = wiki,
+ text = text,
+ summary = summary,
+ added_at = added_at,
+ ip_addr = request.META['REMOTE_ADDR'],
+ )
question.save()
return HttpResponseRedirect(reverse('user_signin_new_question'))
else:
@@ -375,8 +496,8 @@ def question(request, id):
question = get_object_or_404(Question, id=id)
if question.deleted and not can_view_deleted_post(request.user, question):
raise Http404
- answer_form = AnswerForm(question, request.user)
- answers = Answer.objects.get_answers_from_question(question, request.user, orderby)
+ answer_form = AnswerForm(question,request.user)
+ answers = Answer.objects.get_answers_from_question(question, request.user)
answers = answers.select_related(depth=1)
favorited = question.has_favorite_by_user(request.user)
@@ -388,14 +509,14 @@ def question(request, id):
question_vote = question_vote[0]
user_answer_votes = {}
- for vote in question.get_user_votes_in_answers(request.user):
- if not user_answer_votes.has_key(vote.object_id):
+ for answer in answers:
+ vote = answer.get_user_vote(request.user)
+ if vote is not None and not user_answer_votes.has_key(answer.id):
vote_value = -1
if vote.is_upvote():
vote_value = 1
- user_answer_votes[vote.object_id] = vote_value
+ user_answer_votes[answer.id] = vote_value
- #do we need this?
if answers is not None:
answers = answers.order_by("-accepted", orderby)
@@ -442,28 +563,28 @@ def question(request, id):
question_view.save()
return render_to_response('question.html', {
- "question": question,
- "question_vote": question_vote,
- "question_comment_count":question.comments.count(),
- "answer": answer_form,
- "answers": page_objects.object_list,
- "user_answer_votes": user_answer_votes,
- "tags": question.tags.all(),
- "tab_id": view_id,
- "favorited": favorited,
- "similar_questions": Question.objects.get_similar_questions(question),
- "context": {
- 'is_paginated': True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': page_objects.has_previous(),
- 'has_next': page_objects.has_next(),
- 'previous': page_objects.previous_page_number(),
- 'next': page_objects.next_page_number(),
- 'base_url': request.path + '?sort=%s&' % view_id,
- 'extend_url': "#sort-top"
- }
- }, context_instance=RequestContext(request))
+ "question" : question,
+ "question_vote" : question_vote,
+ "question_comment_count":question.comments.count(),
+ "answer" : answer_form,
+ "answers" : page_objects.object_list,
+ "user_answer_votes": user_answer_votes,
+ "tags" : question.tags.all(),
+ "tab_id" : view_id,
+ "favorited" : favorited,
+ "similar_questions" : Question.objects.get_similar_questions(question),
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': page_objects.has_previous(),
+ 'has_next': page_objects.has_next(),
+ 'previous': page_objects.previous_page_number(),
+ 'next': page_objects.next_page_number(),
+ 'base_url' : request.path + '?sort=%s&' % view_id,
+ 'extend_url' : "#sort-top"
+ }
+ }, context_instance=RequestContext(request))
@login_required
def close(request, id):
@@ -483,9 +604,9 @@ def close(request, id):
else:
form = CloseForm()
return render_to_response('close.html', {
- 'form': form,
- 'question': question,
- }, context_instance=RequestContext(request))
+ 'form' : form,
+ 'question' : question,
+ }, context_instance=RequestContext(request))
@login_required
def reopen(request, id):
@@ -493,14 +614,14 @@ def reopen(request, id):
# open question
if not can_reopen_question(request.user, question):
return HttpResponse('Permission denied.')
- if request.method == 'POST':
+ if request.method == 'POST' :
Question.objects.filter(id=question.id).update(closed=False,
- closed_by=None, closed_at=None, close_reason=None)
+ closed_by=None, closed_at=None, close_reason=None)
return HttpResponseRedirect(question.get_absolute_url())
else:
return render_to_response('reopen.html', {
- 'question': question,
- }, context_instance=RequestContext(request))
+ 'question' : question,
+ }, context_instance=RequestContext(request))
@login_required
def edit_question(request, id):
@@ -523,25 +644,25 @@ def _retag_question(request, question):
retagged_at = datetime.datetime.now()
# Update the Question itself
Question.objects.filter(id=question.id).update(
- tagnames=form.cleaned_data['tags'],
- last_edited_at=retagged_at,
- last_edited_by=request.user,
- last_activity_at=retagged_at,
- last_activity_by=request.user
- )
+ tagnames = form.cleaned_data['tags'],
+ last_edited_at = retagged_at,
+ last_edited_by = request.user,
+ last_activity_at = retagged_at,
+ last_activity_by = request.user
+ )
# Update the Question's tag associations
tags_updated = Question.objects.update_tags(question,
- form.cleaned_data['tags'], request.user)
+ form.cleaned_data['tags'], request.user)
# Create a new revision
QuestionRevision.objects.create(
- question=question,
- title=latest_revision.title,
- author=request.user,
- revised_at=retagged_at,
- tagnames=form.cleaned_data['tags'],
- summary=CONST['retagged'],
- text=latest_revision.text
- )
+ question = question,
+ title = latest_revision.title,
+ author = request.user,
+ revised_at = retagged_at,
+ tagnames = form.cleaned_data['tags'],
+ summary = CONST['retagged'],
+ text = latest_revision.text
+ )
# send tags updated singal
tags_updated.send(sender=question.__class__, question=question)
@@ -549,11 +670,10 @@ def _retag_question(request, question):
else:
form = RetagQuestionForm(question)
return render_to_response('question_retag.html', {
- 'question': question,
- 'form': form,
- 'tags': _get_tags_cache_json(),
- }, context_instance=RequestContext(request))
-
+ 'question': question,
+ 'form' : form,
+ 'tags' : _get_tags_cache_json(),
+ }, context_instance=RequestContext(request))
def _edit_question(request, question):
latest_revision = question.get_latest_revision()
@@ -565,8 +685,8 @@ def _edit_question(request, question):
if revision_form.is_valid():
# Replace with those from the selected revision
form = EditQuestionForm(question,
- QuestionRevision.objects.get(question=question,
- revision=revision_form.cleaned_data['revision']))
+ QuestionRevision.objects.get(question=question,
+ revision=revision_form.cleaned_data['revision']))
else:
form = EditQuestionForm(question, latest_revision, request.POST)
else:
@@ -600,20 +720,20 @@ def _edit_question(request, question):
updated_fields['wikified_at'] = edited_at
Question.objects.filter(
- id=question.id).update(** updated_fields)
+ id=question.id).update(**updated_fields)
# Update the Question's tag associations
if tags_changed:
tags_updated = Question.objects.update_tags(
- question, form.cleaned_data['tags'], request.user)
+ question, form.cleaned_data['tags'], request.user)
# Create a new revision
revision = QuestionRevision(
- question=question,
- title=form.cleaned_data['title'],
- author=request.user,
- revised_at=edited_at,
- tagnames=form.cleaned_data['tags'],
- text=form.cleaned_data['text'],
- )
+ question = question,
+ title = form.cleaned_data['title'],
+ author = request.user,
+ revised_at = edited_at,
+ tagnames = form.cleaned_data['tags'],
+ text = form.cleaned_data['text'],
+ )
if form.cleaned_data['summary']:
revision.summary = form.cleaned_data['summary']
else:
@@ -626,11 +746,11 @@ def _edit_question(request, question):
revision_form = RevisionForm(question, latest_revision)
form = EditQuestionForm(question, latest_revision)
return render_to_response('question_edit.html', {
- 'question': question,
- 'revision_form': revision_form,
- 'form': form,
- 'tags': _get_tags_cache_json()
- }, context_instance=RequestContext(request))
+ 'question': question,
+ 'revision_form': revision_form,
+ 'form' : form,
+ 'tags' : _get_tags_cache_json()
+ }, context_instance=RequestContext(request))
@login_required
@@ -664,7 +784,7 @@ def edit_answer(request, id):
'last_edited_by': request.user,
'html': html,
}
- Answer.objects.filter(id=answer.id).update(** updated_fields)
+ Answer.objects.filter(id=answer.id).update(**updated_fields)
revision = AnswerRevision(
answer=answer,
@@ -705,7 +825,7 @@ def question_revisions(request, id):
'title': revision.title,
'html': sanitize_html(markdowner.convert(revision.text)),
'tags': ' '.join(['<a class="post-tag">%s</a>' % tag
- for tag in revision.tagnames.split(' ')]),
+ for tag in revision.tagnames.split(' ')]),
}
if i > 0:
revisions[i].diff = htmldiff(revisions[i-1].html, revision.html)
@@ -787,14 +907,14 @@ def tags(request):
if request.method == "GET":
stag = request.GET.get("q", "").strip()
- if len(stag) > 0:
+ if stag != '':
objects_list = Paginator(Tag.objects.filter(deleted=False).exclude(used_count=0).extra(where=['name like %s'], params=['%' + stag + '%']), DEFAULT_PAGE_SIZE)
else:
- if sortby == "used":
- sortby = "-used_count"
+ if sortby == "name":
+ objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE)
else:
- sortby = "name"
- objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by(sortby), DEFAULT_PAGE_SIZE)
+ objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE)
+
try:
tags = objects_list.page(page)
except (EmptyPage, InvalidPage):
@@ -814,8 +934,7 @@ def tags(request):
'previous': tags.previous_page_number(),
'next': tags.next_page_number(),
'base_url' : reverse('tags') + '?sort=%s&' % sortby
- }
- }, context_instance=RequestContext(request))
+ }})
def tag(request, tag):
return questions(request, tagname=tag)
@@ -861,9 +980,9 @@ def vote(request, id):
response_data = {
"allowed": 1,
"success": 1,
- "status": 0,
- "count": 0,
- "message": ''
+ "status" : 0,
+ "count" : 0,
+ "message" : ''
}
def can_vote(vote_score, user):
@@ -1049,10 +1168,46 @@ def vote(request, id):
data = simplejson.dumps(response_data)
return HttpResponse(data, mimetype="application/json")
+@ajax_login_required
+def mark_tag(request, tag=None, **kwargs):
+ action = kwargs['action']
+ ts = MarkedTag.objects.filter(user=request.user, tag__name=tag)
+ if action == 'remove':
+ logging.debug('deleting tag %s' % tag)
+ ts.delete()
+ else:
+ reason = kwargs['reason']
+ if len(ts) == 0:
+ try:
+ t = Tag.objects.get(name=tag)
+ mt = MarkedTag(user=request.user, reason=reason, tag=t)
+ mt.save()
+ except:
+ pass
+ else:
+ ts.update(reason=reason)
+ return HttpResponse(simplejson.dumps(''), mimetype="application/json")
+
+@ajax_login_required
+def ajax_toggle_ignored_questions(request):
+ if request.user.hide_ignored_questions:
+ new_hide_setting = False
+ else:
+ new_hide_setting = True
+ request.user.hide_ignored_questions = new_hide_setting
+ request.user.save()
+
+@ajax_method
+def ajax_command(request):
+ if 'command' not in request.POST:
+ return HttpResponseForbidden(mimetype="application/json")
+ if request.POST['command'] == 'toggle-ignored-questions':
+ return ajax_toggle_ignored_questions(request)
+
def users(request):
is_paginated = True
sortby = request.GET.get('sort', 'reputation')
- suser = request.REQUEST.get('q', "")
+ suser = request.REQUEST.get('q', "")
try:
page = int(request.GET.get('page', '1'))
except ValueError:
@@ -1080,22 +1235,22 @@ def users(request):
users = objects_list.page(objects_list.num_pages)
return render_to_response('users.html', {
- "users": users,
- "suser": suser,
- "keywords": suser,
- "tab_id": sortby,
- "context": {
- 'is_paginated': is_paginated,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': users.has_previous(),
- 'has_next': users.has_next(),
- 'previous': users.previous_page_number(),
- 'next': users.next_page_number(),
- 'base_url': base_url
- }
-
- }, context_instance=RequestContext(request))
+ "users" : users,
+ "suser" : suser,
+ "keywords" : suser,
+ "tab_id" : sortby,
+ "context" : {
+ 'is_paginated' : is_paginated,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': users.has_previous(),
+ 'has_next': users.has_next(),
+ 'previous': users.previous_page_number(),
+ 'next': users.next_page_number(),
+ 'base_url' : base_url
+ }
+
+ }, context_instance=RequestContext(request))
def user(request, id):
sort = request.GET.get('sort', 'stats')
@@ -1162,43 +1317,43 @@ def edit_user(request, id):
def user_stats(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
questions = Question.objects.extra(
- select={
- 'vote_count': 'question.score',
- 'favorited_myself': 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id',
- 'la_user_id': 'auth_user.id',
- 'la_username': 'auth_user.username',
- 'la_user_gold': 'auth_user.gold',
- 'la_user_silver': 'auth_user.silver',
- 'la_user_bronze': 'auth_user.bronze',
- 'la_user_reputation': 'auth_user.reputation'
- },
- select_params=[user_id],
- tables=['question', 'auth_user'],
- where=['question.deleted = 0 AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'],
- params=[user_id],
- order_by=['-vote_count', '-last_activity_at']
- ).values('vote_count',
- 'favorited_myself',
- 'id',
- 'title',
- 'author_id',
- 'added_at',
- 'answer_accepted',
- 'answer_count',
- 'comment_count',
- 'view_count',
- 'favourite_count',
- 'summary',
- 'tagnames',
- 'vote_up_count',
- 'vote_down_count',
- 'last_activity_at',
- 'la_user_id',
- 'la_username',
- 'la_user_gold',
- 'la_user_silver',
- 'la_user_bronze',
- 'la_user_reputation')[:100]
+ select={
+ 'vote_count' : 'question.score',
+ 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s AND f.question_id = question.id',
+ 'la_user_id' : 'auth_user.id',
+ 'la_username' : 'auth_user.username',
+ 'la_user_gold' : 'auth_user.gold',
+ 'la_user_silver' : 'auth_user.silver',
+ 'la_user_bronze' : 'auth_user.bronze',
+ 'la_user_reputation' : 'auth_user.reputation'
+ },
+ select_params=[user_id],
+ tables=['question', 'auth_user'],
+ where=['question.deleted = 0 AND question.author_id=%s AND question.last_activity_by_id = auth_user.id'],
+ params=[user_id],
+ order_by=['-vote_count', '-last_activity_at']
+ ).values('vote_count',
+ 'favorited_myself',
+ 'id',
+ 'title',
+ 'author_id',
+ 'added_at',
+ 'answer_accepted',
+ 'answer_count',
+ 'comment_count',
+ 'view_count',
+ 'favourite_count',
+ 'summary',
+ 'tagnames',
+ 'vote_up_count',
+ 'vote_down_count',
+ 'last_activity_at',
+ 'la_user_id',
+ 'la_username',
+ 'la_user_gold',
+ 'la_user_silver',
+ 'la_user_bronze',
+ 'la_user_reputation')[:100]
answered_questions = Question.objects.extra(
select={
@@ -1276,8 +1431,7 @@ def user_stats(request, user_id, user_view):
else:
moderate_user_form = None
- return render_to_response(user_view.template_file,
- {
+ return render_to_response(user_view.template_file,{
'moderate_user_form': moderate_user_form,
"tab_name" : user_view.id,
"tab_description" : user_view.tab_description,
@@ -1291,6 +1445,7 @@ def user_stats(request, user_id, user_view):
"votes_today_left": votes_total-votes_today,
"votes_total_per_day": votes_total,
"user_tags" : user_tags[:50],
+ "tags" : tags,
"awards": awards,
"total_awards" : total_awards,
}, context_instance=RequestContext(request))
@@ -1313,6 +1468,7 @@ def user_recent(request, user_id, user_view):
self.title_link = reverse('question', kwargs={'id':question_id}) + u'%s' % slug_title
if int(answer_id) > 0:
self.title_link += '#%s' % answer_id
+
class AwardEvent:
def __init__(self, time, type, id):
self.time = time
@@ -1514,41 +1670,40 @@ def user_recent(request, user_id, user_view):
'added_at',
'activity_type',
)
-
if len(accept_answers) > 0:
accept_answers = [(Event(q['added_at'], q['activity_type'], q['title'], '', '0', \
- q['question_id'])) for q in accept_answers]
+ q['question_id'])) for q in accept_answers]
activities.extend(accept_answers)
#award history
awards = Activity.objects.extra(
- select={
- 'badge_id': 'badge.id',
- 'awarded_at': 'award.awarded_at',
- 'activity_type': 'activity.activity_type'
- },
- tables=['activity', 'award', 'badge'],
- where=['activity.user_id = award.user_id AND activity.user_id = %s AND ' +
- 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'],
- params=[user_id, TYPE_ACTIVITY_PRIZE],
- order_by=['-activity.active_at']
- ).values(
- 'badge_id',
- 'awarded_at',
- 'activity_type'
- )
+ select={
+ 'badge_id' : 'badge.id',
+ 'awarded_at': 'award.awarded_at',
+ 'activity_type' : 'activity.activity_type'
+ },
+ tables=['activity', 'award', 'badge'],
+ where=['activity.user_id = award.user_id AND activity.user_id = %s AND '+
+ 'award.badge_id=badge.id AND activity.object_id=award.id AND activity.activity_type=%s'],
+ params=[user_id, TYPE_ACTIVITY_PRIZE],
+ order_by=['-activity.active_at']
+ ).values(
+ 'badge_id',
+ 'awarded_at',
+ 'activity_type'
+ )
if len(awards) > 0:
awards = [(AwardEvent(q['awarded_at'], q['activity_type'], q['badge_id'])) for q in awards]
activities.extend(awards)
- activities.sort(lambda x, y: cmp(y.time, x.time))
+ activities.sort(lambda x,y: cmp(y.time, x.time))
- return render_to_response(user_view.template_file, {
- "tab_name": user_view.id,
- "tab_description": user_view.tab_description,
- "page_title": user_view.page_title,
- "view_user": user,
- "activities": activities[:user_view.data_size]
- }, context_instance=RequestContext(request))
+ return render_to_response(user_view.template_file,{
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "activities" : activities[:user_view.data_size]
+ }, context_instance=RequestContext(request))
def user_responses(request, user_id, user_view):
"""
@@ -1570,139 +1725,139 @@ def user_responses(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
responses = []
answers = Answer.objects.extra(
- select={
- 'title': 'question.title',
- 'question_id': 'question.id',
- 'answer_id': 'answer.id',
- 'added_at': 'answer.added_at',
- 'html': 'answer.html',
- 'username': 'auth_user.username',
- 'user_id': 'auth_user.id'
- },
- select_params=[user_id],
- tables=['answer', 'question', 'auth_user'],
- where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND ' +
- 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'],
- params=[user_id, user_id],
- order_by=['-answer.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'added_at',
- 'html',
- 'username',
- 'user_id'
- )
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'answer.added_at',
+ 'html' : 'answer.html',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ select_params=[user_id],
+ tables=['answer', 'question', 'auth_user'],
+ where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+
+ 'question.author_id = %s AND answer.author_id <> %s AND answer.author_id=auth_user.id'],
+ params=[user_id, user_id],
+ order_by=['-answer.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'added_at',
+ 'html',
+ 'username',
+ 'user_id'
+ )
if len(answers) > 0:
answers = [(Response(TYPE_RESPONSE['QUESTION_ANSWERED'], a['title'], a['question_id'],
- a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
+ a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
responses.extend(answers)
# question comments
comments = Comment.objects.extra(
- select={
- 'title': 'question.title',
- 'question_id': 'comment.object_id',
- 'added_at': 'comment.added_at',
- 'comment': 'comment.comment',
- 'username': 'auth_user.username',
- 'user_id': 'auth_user.id'
- },
- tables=['question', 'auth_user', 'comment'],
- where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND ' +
- 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'],
- params=[user_id, question_type_id, user_id],
- order_by=['-comment.added_at']
- ).values(
- 'title',
- 'question_id',
- 'added_at',
- 'comment',
- 'username',
- 'user_id'
- )
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'comment.object_id',
+ 'added_at' : 'comment.added_at',
+ 'comment' : 'comment.comment',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ tables=['question', 'auth_user', 'comment'],
+ where=['question.deleted = 0 AND question.author_id = %s AND comment.object_id=question.id AND '+
+ 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id'],
+ params=[user_id, question_type_id, user_id],
+ order_by=['-comment.added_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'added_at',
+ 'comment',
+ 'username',
+ 'user_id'
+ )
if len(comments) > 0:
comments = [(Response(TYPE_RESPONSE['QUESTION_COMMENTED'], c['title'], c['question_id'],
- '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
+ '', c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
responses.extend(comments)
# answer comments
comments = Comment.objects.extra(
- select={
- 'title': 'question.title',
- 'question_id': 'question.id',
- 'answer_id': 'answer.id',
- 'added_at': 'comment.added_at',
- 'comment': 'comment.comment',
- 'username': 'auth_user.username',
- 'user_id': 'auth_user.id'
- },
- tables=['answer', 'auth_user', 'comment', 'question'],
- where=['answer.deleted = 0 AND answer.author_id = %s AND comment.object_id=answer.id AND ' +
- 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id ' +
- 'AND question.id = answer.question_id'],
- params=[user_id, answer_type_id, user_id],
- order_by=['-comment.added_at']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'added_at',
- 'comment',
- 'username',
- 'user_id'
- )
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'comment.added_at',
+ 'comment' : 'comment.comment',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ tables=['answer', 'auth_user', 'comment', 'question'],
+ where=['answer.deleted = 0 AND answer.author_id = %s AND comment.object_id=answer.id AND '+
+ 'comment.content_type_id=%s AND comment.user_id <> %s AND comment.user_id = auth_user.id '+
+ 'AND question.id = answer.question_id'],
+ params=[user_id, answer_type_id, user_id],
+ order_by=['-comment.added_at']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'added_at',
+ 'comment',
+ 'username',
+ 'user_id'
+ )
if len(comments) > 0:
comments = [(Response(TYPE_RESPONSE['ANSWER_COMMENTED'], c['title'], c['question_id'],
- c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
+ c['answer_id'], c['added_at'], c['username'], c['user_id'], c['comment'])) for c in comments]
responses.extend(comments)
# answer has been accepted
answers = Answer.objects.extra(
- select={
- 'title': 'question.title',
- 'question_id': 'question.id',
- 'answer_id': 'answer.id',
- 'added_at': 'answer.accepted_at',
- 'html': 'answer.html',
- 'username': 'auth_user.username',
- 'user_id': 'auth_user.id'
- },
- select_params=[user_id],
- tables=['answer', 'question', 'auth_user'],
- where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND ' +
- 'answer.author_id = %s AND answer.accepted=1 AND question.author_id=auth_user.id'],
- params=[user_id],
- order_by=['-answer.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'added_at',
- 'html',
- 'username',
- 'user_id'
- )
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'added_at' : 'answer.accepted_at',
+ 'html' : 'answer.html',
+ 'username' : 'auth_user.username',
+ 'user_id' : 'auth_user.id'
+ },
+ select_params=[user_id],
+ tables=['answer', 'question', 'auth_user'],
+ where=['answer.question_id = question.id AND answer.deleted=0 AND question.deleted = 0 AND '+
+ 'answer.author_id = %s AND answer.accepted=1 AND question.author_id=auth_user.id'],
+ params=[user_id],
+ order_by=['-answer.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'added_at',
+ 'html',
+ 'username',
+ 'user_id'
+ )
if len(answers) > 0:
answers = [(Response(TYPE_RESPONSE['ANSWER_ACCEPTED'], a['title'], a['question_id'],
- a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
+ a['answer_id'], a['added_at'], a['username'], a['user_id'], a['html'])) for a in answers]
responses.extend(answers)
# sort posts by time
- responses.sort(lambda x, y: cmp(y.time, x.time))
+ responses.sort(lambda x,y: cmp(y.time, x.time))
- return render_to_response(user_view.template_file, {
- "tab_name": user_view.id,
- "tab_description": user_view.tab_description,
- "page_title": user_view.page_title,
- "view_user": user,
- "responses": responses[:user_view.data_size],
+ return render_to_response(user_view.template_file,{
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "responses" : responses[:user_view.data_size],
- }, context_instance=RequestContext(request))
+ }, context_instance=RequestContext(request))
def user_votes(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
@@ -1710,61 +1865,61 @@ def user_votes(request, user_id, user_view):
raise Http404
votes = []
question_votes = Vote.objects.extra(
- select={
- 'title': 'question.title',
- 'question_id': 'question.id',
- 'answer_id': 0,
- 'voted_at': 'vote.voted_at',
- 'vote': 'vote',
- },
- select_params=[user_id],
- tables=['vote', 'question', 'auth_user'],
- where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id ' +
- 'AND vote.user_id=auth_user.id'],
- params=[question_type_id, user_id],
- order_by=['-vote.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'voted_at',
- 'vote',
- )
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 0,
+ 'voted_at' : 'vote.voted_at',
+ 'vote' : 'vote',
+ },
+ select_params=[user_id],
+ tables=['vote', 'question', 'auth_user'],
+ where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = question.id '+
+ 'AND vote.user_id=auth_user.id'],
+ params=[question_type_id, user_id],
+ order_by=['-vote.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'voted_at',
+ 'vote',
+ )
if(len(question_votes) > 0):
votes.extend(question_votes)
answer_votes = Vote.objects.extra(
- select={
- 'title': 'question.title',
- 'question_id': 'question.id',
- 'answer_id': 'answer.id',
- 'voted_at': 'vote.voted_at',
- 'vote': 'vote',
- },
- select_params=[user_id],
- tables=['vote', 'answer', 'question', 'auth_user'],
- where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id ' +
- 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'],
- params=[answer_type_id, user_id],
- order_by=['-vote.id']
- ).values(
- 'title',
- 'question_id',
- 'answer_id',
- 'voted_at',
- 'vote',
- )
+ select={
+ 'title' : 'question.title',
+ 'question_id' : 'question.id',
+ 'answer_id' : 'answer.id',
+ 'voted_at' : 'vote.voted_at',
+ 'vote' : 'vote',
+ },
+ select_params=[user_id],
+ tables=['vote', 'answer', 'question', 'auth_user'],
+ where=['vote.content_type_id = %s AND vote.user_id = %s AND vote.object_id = answer.id '+
+ 'AND answer.question_id = question.id AND vote.user_id=auth_user.id'],
+ params=[answer_type_id, user_id],
+ order_by=['-vote.id']
+ ).values(
+ 'title',
+ 'question_id',
+ 'answer_id',
+ 'voted_at',
+ 'vote',
+ )
if(len(answer_votes) > 0):
votes.extend(answer_votes)
- votes.sort(lambda x, y: cmp(y['voted_at'], x['voted_at']))
- return render_to_response(user_view.template_file, {
- "tab_name": user_view.id,
- "tab_description": user_view.tab_description,
- "page_title": user_view.page_title,
- "view_user": user,
- "votes": votes[:user_view.data_size]
+ votes.sort(lambda x,y: cmp(y['voted_at'], x['voted_at']))
+ return render_to_response(user_view.template_file,{
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "view_user" : user,
+ "votes" : votes[:user_view.data_size]
- }, context_instance=RequestContext(request))
+ }, context_instance=RequestContext(request))
def user_reputation(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
@@ -1789,14 +1944,12 @@ def user_reputation(request, user_id, user_view):
params=[user.id]
).values('positive', 'negative', 'question_id', 'title', 'reputed_at', 'reputation')
reputation.query.group_by = ['question_id']
-
rep_list = []
for rep in Repute.objects.filter(user=user).order_by('reputed_at'):
dic = '[%s,%s]' % (calendar.timegm(rep.reputed_at.timetuple()) * 1000, rep.reputation)
rep_list.append(dic)
reps = ','.join(rep_list)
reps = '[%s]' % reps
-
return render_to_response(user_view.template_file, {
"tab_name": user_view.id,
"tab_description": user_view.tab_description,
@@ -1809,81 +1962,84 @@ def user_reputation(request, user_id, user_view):
def user_favorites(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
questions = Question.objects.extra(
- select={
- 'vote_count': 'question.vote_up_count + question.vote_down_count',
- 'favorited_myself': 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s ' +
- 'AND f.question_id = question.id',
- 'la_user_id': 'auth_user.id',
- 'la_username': 'auth_user.username',
- 'la_user_gold': 'auth_user.gold',
- 'la_user_silver': 'auth_user.silver',
- 'la_user_bronze': 'auth_user.bronze',
- 'la_user_reputation': 'auth_user.reputation'
- },
- select_params=[user_id],
- tables=['question', 'auth_user', 'favorite_question'],
- where=['question.deleted = 0 AND question.last_activity_by_id = auth_user.id ' +
- 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'],
- params=[user_id],
- order_by=['-vote_count', '-question.id']
- ).values('vote_count',
- 'favorited_myself',
- 'id',
- 'title',
- 'author_id',
- 'added_at',
- 'answer_accepted',
- 'answer_count',
- 'comment_count',
- 'view_count',
- 'favourite_count',
- 'summary',
- 'tagnames',
- 'vote_up_count',
- 'vote_down_count',
- 'last_activity_at',
- 'la_user_id',
- 'la_username',
- 'la_user_gold',
- 'la_user_silver',
- 'la_user_bronze',
- 'la_user_reputation')
- return render_to_response(user_view.template_file, {
- "tab_name": user_view.id,
- "tab_description": user_view.tab_description,
- "page_title": user_view.page_title,
- "questions": questions[:user_view.data_size],
- "view_user": user
- }, context_instance=RequestContext(request))
-
+ select={
+ 'vote_count' : 'question.vote_up_count + question.vote_down_count',
+ 'favorited_myself' : 'SELECT count(*) FROM favorite_question f WHERE f.user_id = %s '+
+ 'AND f.question_id = question.id',
+ 'la_user_id' : 'auth_user.id',
+ 'la_username' : 'auth_user.username',
+ 'la_user_gold' : 'auth_user.gold',
+ 'la_user_silver' : 'auth_user.silver',
+ 'la_user_bronze' : 'auth_user.bronze',
+ 'la_user_reputation' : 'auth_user.reputation'
+ },
+ select_params=[user_id],
+ tables=['question', 'auth_user', 'favorite_question'],
+ where=['question.deleted = 0 AND question.last_activity_by_id = auth_user.id '+
+ 'AND favorite_question.question_id = question.id AND favorite_question.user_id = %s'],
+ params=[user_id],
+ order_by=['-vote_count', '-question.id']
+ ).values('vote_count',
+ 'favorited_myself',
+ 'id',
+ 'title',
+ 'author_id',
+ 'added_at',
+ 'answer_accepted',
+ 'answer_count',
+ 'comment_count',
+ 'view_count',
+ 'favourite_count',
+ 'summary',
+ 'tagnames',
+ 'vote_up_count',
+ 'vote_down_count',
+ 'last_activity_at',
+ 'la_user_id',
+ 'la_username',
+ 'la_user_gold',
+ 'la_user_silver',
+ 'la_user_bronze',
+ 'la_user_reputation')
+ return render_to_response(user_view.template_file,{
+ "tab_name" : user_view.id,
+ "tab_description" : user_view.tab_description,
+ "page_title" : user_view.page_title,
+ "questions" : questions[:user_view.data_size],
+ "view_user" : user
+ }, context_instance=RequestContext(request))
def user_email_subscriptions(request, user_id, user_view):
user = get_object_or_404(User, id=user_id)
if request.method == 'POST':
- form = EditUserEmailFeedsForm(request.POST)
- if form.is_valid():
+ email_feeds_form = EditUserEmailFeedsForm(request.POST)
+ tag_filter_form = TagFilterSelectionForm(request.POST, instance=user)
+ if email_feeds_form.is_valid() and tag_filter_form.is_valid():
+ tag_filter_form.save()
if 'save' in request.POST:
- saved = form.save(user)
+ saved = email_feeds_form.save(user)
if saved:
action_status = _('changes saved')
elif 'stop_email' in request.POST:
- saved = form.reset().save(user)
+ saved = email_feeds_form.reset().save(user)
initial_values = EditUserEmailFeedsForm.NO_EMAIL_INITIAL
- form = EditUserEmailFeedsForm(initial=initial_values)
+ email_feeds_form = EditUserEmailFeedsForm(initial=initial_values)
if saved:
action_status = _('email updates canceled')
if not saved:
action_status = None
else:
- form = EditUserEmailFeedsForm()
- form.set_initial_values(user)
+ email_feeds_form = EditUserEmailFeedsForm()
+ email_feeds_form.set_initial_values(user)
+ tag_filter_form = TagFilterSelectionForm(instance=user)
action_status = None
return render_to_response(user_view.template_file,{
'tab_name':user_view.id,
'tab_description':user_view.tab_description,
'page_title':user_view.page_title,
'view_user':user,
- 'email_feeds_form':form,
+ 'email_feeds_form':email_feeds_form,
+ 'tag_filter_selection_form':tag_filter_form,
'action_status':action_status,
}, context_instance=RequestContext(request))
@@ -1979,21 +2135,21 @@ def badges(request):
def badge(request, id):
badge = get_object_or_404(Badge, id=id)
awards = Award.objects.extra(
- select={'id': 'auth_user.id',
- 'name': 'auth_user.username',
- 'rep':'auth_user.reputation',
- 'gold': 'auth_user.gold',
- 'silver': 'auth_user.silver',
- 'bronze': 'auth_user.bronze'},
- tables=['award', 'auth_user'],
- where=['badge_id=%s AND user_id=auth_user.id'],
- params=[id]
- ).values('id').distinct()
+ select={'id': 'auth_user.id',
+ 'name': 'auth_user.username',
+ 'rep':'auth_user.reputation',
+ 'gold': 'auth_user.gold',
+ 'silver': 'auth_user.silver',
+ 'bronze': 'auth_user.bronze'},
+ tables=['award', 'auth_user'],
+ where=['badge_id=%s AND user_id=auth_user.id'],
+ params=[id]
+ ).values('id').distinct()
return render_to_response('badge.html', {
- 'awards': awards,
- 'badge': badge,
- }, context_instance=RequestContext(request))
+ 'awards' : awards,
+ 'badge' : badge,
+ }, context_instance=RequestContext(request))
def read_message(request):
if request.method == "POST":
@@ -2025,8 +2181,8 @@ def upload(request):
if not file_name_suffix in settings.ALLOW_FILE_TYPES:
raise FileTypeNotAllow
- # genetate new file name
- new_file_name = str(time.time()).replace('.', str(random.randint(0, 100000))) + file_name_suffix
+ # generate new file name
+ new_file_name = str(time.time()).replace('.', str(random.randint(0,100000))) + file_name_suffix
# use default storage to store file
default_storage.save(new_file_name, f)
# check file size
@@ -2084,7 +2240,7 @@ def book(request, short_name, unanswered=False):
page = 1
view_id = request.GET.get('sort', None)
- view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score"}
+ view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
try:
orderby = view_dic[view_id]
except KeyError:
@@ -2105,22 +2261,22 @@ def book(request, short_name, unanswered=False):
questions = objects_list.page(page)
return render_to_response('book.html', {
- "book": book,
- "author_info": author_info,
- "author_rss": author_rss,
- "questions": questions,
- "context": {
- 'is_paginated': True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': questions.has_previous(),
- 'has_next': questions.has_next(),
- 'previous': questions.previous_page_number(),
- 'next': questions.next_page_number(),
- 'base_url': request.path + '?sort=%s&' % view_id,
- 'pagesize': user_page_size
- }
- }, context_instance=RequestContext(request))
+ "book" : book,
+ "author_info" : author_info,
+ "author_rss" : author_rss,
+ "questions" : questions,
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': questions.has_previous(),
+ 'has_next': questions.has_next(),
+ 'previous': questions.previous_page_number(),
+ 'next': questions.next_page_number(),
+ 'base_url' : request.path + '?sort=%s&' % view_id,
+ 'pagesize' : user_page_size
+ }
+ }, context_instance=RequestContext(request))
@login_required
def ask_book(request, short_name):
@@ -2130,16 +2286,16 @@ def ask_book(request, short_name):
added_at = datetime.datetime.now()
html = sanitize_html(markdowner.convert(form.cleaned_data['text']))
question = Question(
- title=strip_tags(form.cleaned_data['title']),
- author=request.user,
- added_at=added_at,
- last_activity_at=added_at,
- last_activity_by=request.user,
- wiki=form.cleaned_data['wiki'],
- tagnames=form.cleaned_data['tags'].strip(),
- html=html,
- summary=strip_tags(html)[:120]
- )
+ title = strip_tags(form.cleaned_data['title']),
+ author = request.user,
+ added_at = added_at,
+ last_activity_at = added_at,
+ last_activity_by = request.user,
+ wiki = form.cleaned_data['wiki'],
+ tagnames = form.cleaned_data['tags'].strip(),
+ html = html,
+ summary = strip_tags(html)[:120]
+ )
if question.wiki:
question.last_edited_by = question.author
question.last_edited_at = added_at
@@ -2149,15 +2305,15 @@ def ask_book(request, short_name):
# create the first revision
QuestionRevision.objects.create(
- question=question,
- revision=1,
- title=question.title,
- author=request.user,
- revised_at=added_at,
- tagnames=question.tagnames,
- summary=CONST['default_version'],
- text=form.cleaned_data['text']
- )
+ question = question,
+ revision = 1,
+ title = question.title,
+ author = request.user,
+ revised_at = added_at,
+ tagnames = question.tagnames,
+ summary = CONST['default_version'],
+ text = form.cleaned_data['text']
+ )
books = Book.objects.extra(where=['short_name = %s'], params=[short_name])
match_count = len(books)
@@ -2171,13 +2327,11 @@ def ask_book(request, short_name):
form = AskForm()
tags = _get_tags_cache_json()
- return render_to_response('ask.html',
- {
- 'form' : form,
- 'tags' : tags,
- 'email_validation_faq_url': reverse('faq') + '#validate',
- }, context_instance=RequestContext(request)
- )
+ return render_to_response('ask.html', {
+ 'form' : form,
+ 'tags' : tags,
+ 'email_validation_faq_url': reverse('faq') + '#validate',
+ }, context_instance=RequestContext(request))
def search(request):
"""
@@ -2228,7 +2382,7 @@ def search(request):
user.save()
view_id = request.GET.get('sort', None)
- view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score"}
+ view_dic = {"latest":"-added_at", "active":"-last_activity_at", "hottest":"-answer_count", "mostvoted":"-score" }
try:
orderby = view_dic[view_id]
except KeyError:
@@ -2257,25 +2411,25 @@ def search(request):
related_tags.append(tag)
return render_to_response(template_file, {
- "questions": questions,
- "tab_id": view_id,
- "questions_count": objects_list.count,
- "tags": related_tags,
- "searchtag": None,
- "searchtitle": keywords,
- "keywords": keywords,
- "is_unanswered": False,
- "context": {
- 'is_paginated': True,
- 'pages': objects_list.num_pages,
- 'page': page,
- 'has_previous': questions.has_previous(),
- 'has_next': questions.has_next(),
- 'previous': questions.previous_page_number(),
- 'next': questions.next_page_number(),
- 'base_url': request.path + '?t=question&q=%s&sort=%s&' % (keywords, view_id),
- 'pagesize': pagesize
- }}, context_instance=RequestContext(request))
+ "questions" : questions,
+ "tab_id" : view_id,
+ "questions_count" : objects_list.count,
+ "tags" : related_tags,
+ "searchtag" : None,
+ "searchtitle" : keywords,
+ "keywords" : keywords,
+ "is_unanswered" : False,
+ "context" : {
+ 'is_paginated' : True,
+ 'pages': objects_list.num_pages,
+ 'page': page,
+ 'has_previous': questions.has_previous(),
+ 'has_next': questions.has_next(),
+ 'previous': questions.previous_page_number(),
+ 'next': questions.next_page_number(),
+ 'base_url' : request.path + '?t=question&q=%s&sort=%s&' % (keywords, view_id),
+ 'pagesize' : pagesize
+ }}, context_instance=RequestContext(request))
else:
raise Http404
diff --git a/settings.py b/settings.py
index e964f092..a32b2303 100644
--- a/settings.py
+++ b/settings.py
@@ -25,7 +25,7 @@ MIDDLEWARE_CLASSES = (
'middleware.anon_user.ConnectToSessionMessagesMiddleware',
'middleware.pagesize.QuestionsPageSizeMiddleware',
'middleware.cancel.CancelActionMiddleware',
- #'debug_toolbar.middleware.DebugToolbarMiddleware',
+ 'debug_toolbar.middleware.DebugToolbarMiddleware',
)
TEMPLATE_CONTEXT_PROCESSORS = (
@@ -62,7 +62,7 @@ INSTALLED_APPS = (
'forum',
'django_authopenid',
'djangosphinx',
- #'debug_toolbar' ,
+ 'debug_toolbar' ,
'user_messages',
)
diff --git a/sql_scripts/091208_upgrade_evgeny.sql b/sql_scripts/091208_upgrade_evgeny.sql
new file mode 100644
index 00000000..d9c4289a
--- /dev/null
+++ b/sql_scripts/091208_upgrade_evgeny.sql
@@ -0,0 +1 @@
+ALTER TABLE `auth_user` add column hide_ignored_questions tinyint(1) not NULL;
diff --git a/sql_scripts/091208_upgrade_evgeny_1.sql b/sql_scripts/091208_upgrade_evgeny_1.sql
new file mode 100644
index 00000000..b1b4107f
--- /dev/null
+++ b/sql_scripts/091208_upgrade_evgeny_1.sql
@@ -0,0 +1 @@
+ALTER TABLE `auth_user` add column `tag_filter_setting` varchar(16) not NULL default 'ignored';
diff --git a/sql_scripts/update_2009_01_25_001.sql b/sql_scripts/update_2009_01_25_001.sql
index f6f3bed2..16c3487b 100644
--- a/sql_scripts/update_2009_01_25_001.sql
+++ b/sql_scripts/update_2009_01_25_001.sql
@@ -1,2 +1,2 @@
ALTER TABLE `award` ADD `content_type_id` INT NULL
-ALTER TABLE `award` ADD `object_id` INT NULL \ No newline at end of file
+ALTER TABLE `award` ADD `object_id` INT NULL
diff --git a/sql_scripts/update_2009_02_26_001.sql b/sql_scripts/update_2009_02_26_001.sql
index 4113d8f5..a6af5931 100644
--- a/sql_scripts/update_2009_02_26_001.sql
+++ b/sql_scripts/update_2009_02_26_001.sql
@@ -16,4 +16,4 @@ WHERE tagnames like '%c#%'
UPDATE question_revision
SET tagnames = replace(tagnames, 'c#', 'csharp')
-WHERE tagnames like '%c#%' \ No newline at end of file
+WHERE tagnames like '%c#%'
diff --git a/sql_scripts/update_2009_04_10_001.sql b/sql_scripts/update_2009_04_10_001.sql
index e1ceb3bc..8148632a 100644
--- a/sql_scripts/update_2009_04_10_001.sql
+++ b/sql_scripts/update_2009_04_10_001.sql
@@ -1,3 +1,3 @@
ALTER TABLE Tag ADD COLUMN deleted_at datetime default null;
ALTER TABLE Tag ADD COLUMN deleted_by_id INTEGER NULL;
-ALTER TABLE Tag ADD COLUMN deleted TINYINT NOT NULL; \ No newline at end of file
+ALTER TABLE Tag ADD COLUMN deleted TINYINT NOT NULL;
diff --git a/templates/authopenid/complete.html b/templates/authopenid/complete.html
index 9a94c3c4..ce5fb7fe 100644
--- a/templates/authopenid/complete.html
+++ b/templates/authopenid/complete.html
@@ -88,6 +88,7 @@ parameters:
</div>
<p class='nomargin'>{% trans "receive updates motivational blurb" %}</p>
{% include "edit_user_email_feeds_form.html" %}
+ <p class='nomargin'>{% trans "Tag filter tool will be your right panel, once you log in." %}</p>
<div class="submit-row"><input type="submit" class="submit" name="bnewaccount" value="{% trans "create account" %}"/></div>
</form>
</div>
diff --git a/templates/base.html b/templates/base.html
index ec0a53d7..daafc3bc 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -75,7 +75,6 @@
{% endblock%}
</div>
-
<div id="CARight">
{% block sidebar%}
{% endblock%}
diff --git a/templates/content/images/close-small-dark.png b/templates/content/images/close-small-dark.png
new file mode 100644
index 00000000..280c1fc7
--- /dev/null
+++ b/templates/content/images/close-small-dark.png
Binary files differ
diff --git a/templates/content/js/com.cnprog.admin.js b/templates/content/js/com.cnprog.admin.js
index 73b5768f..cb1c1b15 100644
--- a/templates/content/js/com.cnprog.admin.js
+++ b/templates/content/js/com.cnprog.admin.js
@@ -3,7 +3,7 @@ $().ready( function(){
success: function(a,b){$('.admin #action_status').html($.i18n._('changes saved'));},
dataType:'json',
timeout:5000,
- url: $.i18n._('/') + $.i18n._('moderate-user/') + viewUserID + '/'
+ url: scriptUrl + $.i18n._('moderate-user/') + viewUserID + '/'
};
var form = $('.admin #moderate_user_form').ajaxForm(options);
var box = $('.admin input#id_is_approved').click(function(){
diff --git a/templates/content/js/com.cnprog.post.js b/templates/content/js/com.cnprog.post.js
index 5d58ff21..0d4c52d3 100644
--- a/templates/content/js/com.cnprog.post.js
+++ b/templates/content/js/com.cnprog.post.js
@@ -500,7 +500,7 @@ function createComments(type) {
jDiv.append('<p id="' + divId + '" class="comment">'
+ $.i18n._('to comment, need') + ' ' +
+ repNeededForComments + ' ' + $.i18n._('community karma points')
- + '<a href="' + $.i18n._('/') + $.i18n._('faq/') + '" class="comment-user">'
+ + '<a href="' + scriptUrl + $.i18n._('faq/') + '" class="comment-user">'
+ $.i18n._('please see') + 'faq</a></span></p>');
}
}
@@ -601,7 +601,7 @@ function createComments(type) {
$(this).children().each(
function(i){
var comment_id = $(this).attr('id').replace('comment-','');
- var delete_url = $.i18n._('/') + objectType + 's/' + post_id + '/'
+ var delete_url = scriptUrl + objectType + 's/' + post_id + '/'
+ $.i18n._('comments/') + comment_id + '/' + $.i18n._('delete/');
var html = $(this).html();
var CommentsClass;
@@ -615,12 +615,12 @@ function createComments(type) {
delete_icon.click(function(){CommentsClass.deleteComment($(this),comment_id,delete_url);});
delete_icon.unbind('mouseover').bind('mouseover',
function(){
- $(this).attr('src',$.i18n._('/') + 'content/images/close-small-hover.png');
+ $(this).attr('src',scriptUrl + 'content/images/close-small-hover.png');
}
);
delete_icon.unbind('mouseout').bind('mouseout',
function(){
- $(this).attr('src',$.i18n._('/') + 'content/images/close-small.png');
+ $(this).attr('src',scriptUrl + 'content/images/close-small.png');
}
);
}
@@ -670,12 +670,178 @@ function createComments(type) {
};
}
+function pickedTags(){
+
+ var sendAjax = function(tagname, reason, action, callback){
+ 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 + '/';
+
+ call_settings = {
+ type:'POST',
+ url:url
+ }
+ 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];
+ }
+ 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', scriptUrl + 'content/images/close-small-hover.png');
+ });
+ obj.unbind('mouseout').bind('mouseout', function(){
+ $(this).attr('src', scriptUrl + 'content/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'));
+ to_target = interestingTags;
+ from_target = ignoredTags;
+ 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(){
+ new_tag = $('<span></span>');
+ new_tag.addClass('deletable-tag');
+ tag_link = $('<a></a>');
+ tag_link.attr('rel','tag');
+ tag_link.attr('href', scriptUrl + $.i18n._('tags/') + tagname);
+ tag_link.html(tagname);
+ del_link = $('<img></img>');
+ del_link.addClass('delete-icon');
+ del_link.attr('src', scriptUrl + 'content/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;
+ });
+ }
+ }
+
+ 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){
+ item_id = $(item).attr('id')
+ 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(tags, {
+ minChars: 1,
+ matchContains: true,
+ max: 20,
+ multiple: true,
+ multipleSeparator: " ",
+ formatItem: function(row, i, max) {
+ return row.n + " ("+ row.c +")";
+ },
+ formatResult: function(row, i, max){
+ return row.n;
+ }
+
+ });
+ $("#interestingTagAdd").click(function(){handlePickedTag(this,'good')});
+ $("#ignoredTagAdd").click(function(){handlePickedTag(this,'bad')});
+ }
+ };
+}
+
var questionComments = createComments('question');
var answerComments = createComments('answer');
$().ready(function() {
questionComments.init();
answerComments.init();
+ pickedTags().init();
});
var commentsFactory = {'question' : questionComments, 'answer' : answerComments};
diff --git a/templates/content/js/com.cnprog.utils.js b/templates/content/js/com.cnprog.utils.js
index cf27c8a1..b19b6773 100644
--- a/templates/content/js/com.cnprog.utils.js
+++ b/templates/content/js/com.cnprog.utils.js
@@ -36,7 +36,7 @@ var notify = function() {
function appendLoader(containerSelector) {
$(containerSelector).append('<img class="ajax-loader" '
- +'src="' + $.i18n._('/') + 'content/images/indicator.gif" title="'
+ +'src="' + scriptUrl + 'content/images/indicator.gif" title="'
+$.i18n._('loading...')
+'" alt="'
+$.i18n._('loading...')
diff --git a/templates/content/js/compress.bat b/templates/content/js/compress.bat
index 41e1882a..5b2673cf 100644
--- a/templates/content/js/compress.bat
+++ b/templates/content/js/compress.bat
@@ -2,5 +2,4 @@
#java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 wmd\showdown.js -o wmd\showdown-min.js
#java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 com.cnprog.post.js -o com.cnprog.post.pack.js
java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 se_hilite_src.js -o se_hilite.js
-
-pause \ No newline at end of file
+pause
diff --git a/templates/content/js/flot-build.bat b/templates/content/js/flot-build.bat
index fc715e3a..f9f32cb7 100644
--- a/templates/content/js/flot-build.bat
+++ b/templates/content/js/flot-build.bat
@@ -1,3 +1,3 @@
java -jar yuicompressor-2.4.2.jar --type js --charset utf-8 jquery.flot.js -o jquery.flot.pack.js
-pause \ No newline at end of file
+pause
diff --git a/templates/content/style/style.css b/templates/content/style/style.css
index 4038e8a1..47b4dc00 100644
--- a/templates/content/style/style.css
+++ b/templates/content/style/style.css
@@ -162,7 +162,7 @@ blockquote
border-right:1px solid #b4b48e;
border-bottom:1px solid #b4b48e;*/
background: white;/* #f9f7ed;*/
- margin:10px 0 10px 0;
+ /*margin:10px 0 10px 0;*/
/*background:url(../images/quest-bg.gif) repeat-x top;*/
}
#listA .qstA thumb {float:left; }
@@ -204,7 +204,14 @@ blockquote
/*border-bottom:1px solid #888a85;*/
}
.evenMore {font-size:14px; font-weight:800;}
-.questions-count{font-size:32px;font-family:sans-serif;font-weight:600;padding:0 0 5px 7px;color:#a40000;}
+.questions-count{
+ font-size:32px;
+ font-family:sans-serif;
+ font-weight:600;
+ padding:0 0 5px 0px;
+ color:#a40000;
+ margin-top:3px;
+}
/*内容块*/
.boxA {background:#888a85; padding:6px; margin-bottom:8px;border 1px solid #babdb6;}
@@ -216,7 +223,7 @@ blockquote
.boxB .body {border:1px solid #aaaaaa; padding:8px; background:#FFF; font-size:13px; line-height:160%;}
.boxB .more {padding:1px; text-align:right; font-weight:800;}
.boxC {
- background:#babdb6;/*f9f7ed;*/
+ background: #cacdc6;/*f9f7ed;*/
padding:10px;
margin-bottom:8px;
border-top:1px solid #eeeeec;
@@ -224,6 +231,12 @@ blockquote
border-right:1px solid #a9aca5;
border-bottom:1px solid #babdb6;
}
+.boxC p {
+ margin-bottom:8px;
+}
+.boxC p.nomargin {
+ margin:0px;
+}
.boxC p.info-box-follow-up-links {
text-align:right;
margin:0;
@@ -308,12 +321,14 @@ blockquote
/*标签*/
.tag {font-size:13px; font-weight:normal; color:#333; text-decoration:none;background-color:#EEE; border-left:3px solid #777; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:1px 8px 1px 8px;}
.tags {font-family:sans-serif; line-height:200%; display:block; margin-top:5px;}
-.tags a {font-size:13px; font-weight:normal; color:#333; text-decoration:none;background-color:#EEE; border-left:3px solid #777; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:1px 8px 1px 8px;}
+.tags a {white-space: nowrap; font-size:13px; font-weight:normal; color:#333; text-decoration:none;background-color:#EEE; border-left:3px solid #777; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:1px 8px 1px 8px;}
.tags a:hover {background-color:#fFF;color:#333;}
.tagsbox {line-height:200%;}
.tagsbox a {font-size:13px; font-weight:normal; color:#333; text-decoration:none;background-color:#EEE; border-left:3px solid #777; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:1px 8px 1px 8px;}
.tagsbox a:hover {background-color:#fFF;color:#333;}
.tag-number {font-weight:700;font-family:sans-serif;}
+.marked-tags { margin-top: 0px;margin-bottom: 5px; }
+.deletable-tag { margin-right: 3px; white-space:nowrap; }
/*奖牌*/
a.medal { font-size:14px; line-height:250%; font-weight:800; color:#333; text-decoration:none; background:url(../images/medala.gif) no-repeat; border-left:1px solid #EEE; border-top:1px solid #EEE; border-bottom:1px solid #CCC; border-right:1px solid #CCC; padding:4px 12px 4px 6px;}
@@ -1422,3 +1437,12 @@ ul.form-horizontal-rows li input {
text-align:center;
font-weight:bold;
}
+#tagSelector {
+ padding-bottom: 2px;
+}
+#hideIgnoredTagsControl {
+ margin: 5px 0 0 0;
+}
+#hideIgnoredTagsCb {
+ margin: 0 2px 0 1px;
+}
diff --git a/templates/index.html b/templates/index.html
index 470612b4..68a13197 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -10,13 +10,15 @@
<meta name="description" content="{{ settings.APP_DESCRIPTION }}" />{% endblock %}
{% block forejs %}
<script type="text/javascript">
- $().ready(function(){
- var tab_id = "{{ tab_id }}";
- $("#"+tab_id).attr('className',"on");
- $("#nav_questions").attr('className',"on");
- });
-
- </script>
+ var tags = {{ tags_autocomplete|safe }};
+ $().ready(function(){
+ var tab_id = "{{ tab_id }}";
+ $("#"+tab_id).attr('className',"on");
+ $("#nav_questions").attr('className',"on");
+ });
+ </script>
+ <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
{% endblock %}
{% block content %}
<div class="tabBar">
@@ -118,6 +120,8 @@
<div class="more"><a href="{% url faq %}">{% trans "faq" %} »</a></div>
</div>
</div>
+{% else %}
+{% include "tag_selector.html" %}
{% endif %}
<div class="boxC">
<h3>{% trans "Recent tags" %}</h3>
diff --git a/templates/questions.html b/templates/questions.html
index 47bda129..63026dc3 100644
--- a/templates/questions.html
+++ b/templates/questions.html
@@ -8,16 +8,19 @@
{% block title %}{% spaceless %}{% trans "Questions" %}{% endspaceless %}{% endblock %}
{% block forejs %}
<script type="text/javascript">
- $().ready(function(){
- var tab_id = "{{ tab_id }}";
- $("#"+tab_id).attr('className',"on");
- $("#nav_questions").attr('className',"on");
- Hilite.exact = false;
- Hilite.elementid = "listA";
- Hilite.debug_referrer = location.href;
- });
-
- </script>
+ var tags = {{ tags_autocomplete|safe }};
+ $().ready(function(){
+ var tab_id = "{{ tab_id }}";
+ $("#"+tab_id).attr('className',"on");
+ var on_tab = {% if is_unanswered %}'#nav_unanswered'{% else %}'#nav_questions'{% endif %};
+ $(on_tab).attr('className','on');
+ Hilite.exact = false;
+ Hilite.elementid = "listA";
+ Hilite.debug_referrer = location.href;
+ });
+ </script>
+ <script type='text/javascript' src='{% href "/content/js/com.cnprog.editor.js" %}'></script>
+ <script type='text/javascript' src='{% href "/content/js/com.cnprog.post.js" %}'></script>
{% endblock %}
{% block content %}
<div class="tabBar">
@@ -32,7 +35,11 @@
{% trans "Found by title" %}
{% endif %}
{% else %}
- {% trans "All questions" %}
+ {% if is_unanswered %}
+ {% trans "Unanswered questions" %}
+ {% else %}
+ {% trans "All questions" %}
+ {% endif %}
{% endif %}
{% endif %}
</div>
@@ -45,7 +52,17 @@
</div>
<div id="listA">
{% for question in questions.object_list %}
- <div class="qstA">
+ <div class="qstA"
+ {% if request.user.is_authenticated %}
+ {% if question.interesting_score > 0 %}
+ style="background:#ffff99;"
+ {% else %}
+ {% if question.ignored_score > 0 %}
+ style="background:#f3f3f3;"
+ {% endif %}
+ {% endif %}
+ {% endif %}
+ >
<h2>
<a href="{{ question.get_absolute_url }}">{{ question.get_question_title }}</a>
</h2>
@@ -134,25 +151,18 @@
{% endblock %}
{% block tail %}
-
- <div class="pager">
- {% cnprog_paginator context %}
-
- </div>
- <div class="pagesize">
- {% cnprog_pagesize context %}
- </div>
-
+ <div class="pager">{% cnprog_paginator context %}</div>
+ <div class="pagesize">{% cnprog_pagesize context %}</div>
{% endblock %}
{% block sidebar %}
<div class="boxC">
{% if searchtag %}
- {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num and searchtag as tagname %}
- have total {{q_num}} questions tagged {{tagname}}
- {% plural %}
- have total {{q_num}} questions tagged {{tagname}}
- {% endblocktrans %}
+ {% blocktrans count questions_count as cnt with questions_count|intcomma as q_num and searchtag as tagname %}
+ have total {{q_num}} questions tagged {{tagname}}
+ {% plural %}
+ have total {{q_num}} questions tagged {{tagname}}
+ {% endblocktrans %}
{% else %}
{% if searchtitle %}
{% if settings.USE_SPHINX_SEARCH %}
@@ -169,14 +179,22 @@
{% endblocktrans %}
{% endif %}
{% else %}
- {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
- have total {{q_num}} questions
- {% plural %}
- have total {{q_num}} questions
- {% endblocktrans %}
+ {% if is_unanswered %}
+ {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} unanswered questions
+ {% plural %}
+ have total {{q_num}} unanswered questions
+ {% endblocktrans %}
+ {% else %}
+ {% blocktrans count questions as cnt with questions_count|intcomma as q_num %}
+ have total {{q_num}} questions
+ {% plural %}
+ have total {{q_num}} questions
+ {% endblocktrans %}
+ {% endif %}
{% endif %}
{% endif %}
- <p>
+ <p class="nomargin">
{% ifequal tab_id "latest" %}
{% trans "latest questions info" %}
{% endifequal %}
@@ -197,15 +215,17 @@
{% endifequal %}
</p>
</div>
+{% if request.user.is_authenticated %}
+{% include "tag_selector.html" %}
+{% endif %}
<div class="boxC">
<h3 class="subtitle">{% trans "Related tags" %}</h3>
<div class="tags">
{% for tag in tags %}
- <a rel="tag" title="{% trans "see questions tagged" %}'{{ tag.name }}'{% trans "using tags" %}" href="{% url forum.views.tag tag.name|urlencode %}">{{ tag.name }}</a>
+ <a rel="tag" title="{% blocktrans with tag.name as tag_name %}see questions tagged '{{ tag_name }}'{% endblocktrans %}" href="{% url forum.views.tag tag.name|urlencode %}">{{ tag.name }}</a>
<span class="tag-number">&#215; {{ tag.used_count|intcomma }}</span>
<br />
{% endfor %}
- <br />
</div>
</div>
diff --git a/templates/tag_selector.html b/templates/tag_selector.html
new file mode 100644
index 00000000..6edc5cc8
--- /dev/null
+++ b/templates/tag_selector.html
@@ -0,0 +1,42 @@
+{% load i18n %}
+{% load extra_tags %}
+<div id="tagSelector" class="boxC">
+ <h3 class="subtitle">{% trans "Interesting tags" %}</h3>
+ <div class="tags interesting marked-tags">
+ {% for tag_name in interesting_tag_names %}
+ {% spaceless %}
+ <span class="deletable-tag" id="interesting-tag-{{tag_name}}">
+ <a rel="tag"
+ title="{% blocktrans with tag as tagname %}see questions tagged '{{ tag_name }}'{% endblocktrans %}"
+ href="{% url tag_questions tag_name|urlencode %}">{{tag_name}}</a>
+ <img class="delete-icon"
+ src="{% href "/content/images/close-small-dark.png" %}"
+ title="{% blocktrans %}remove '{{tag_name}}' from the list of interesting tags{% endblocktrans %}"/>
+ </span>
+ {% endspaceless %}
+ {% endfor %}
+ </div>
+ <input id="interestingTagInput" autocomplete="off" type="text"/>
+ <input id="interestingTagAdd" type="submit" value="{% trans "Add" %}"/>
+ <h3 class="subtitle">{% trans "Ignored tags" %}</h3>
+ <div class="tags ignored marked-tags">
+ {% for tag_name in ignored_tag_names %}
+ {% spaceless %}
+ <span class="deletable-tag" id="ignored-tag-{{tag_name}}">
+ <a rel="tag"
+ title="{% blocktrans with tag as tagname %}see questions tagged '{{ tag_name }}'{% endblocktrans %}"
+ href="{% url tag_questions tag_name|urlencode %}">{{tag_name}}</a>
+ <img class="delete-icon"
+ src="{% href "/content/images/close-small-dark.png" %}"
+ title="{% blocktrans %}remove '{{tag_name}}' from the list of ignored tags{% endblocktrans %}"/>
+ </span>
+ {% endspaceless %}
+ {% endfor %}
+ </div>
+ <input id="ignoredTagInput" autocomplete="off" type="text"/>
+ <input id="ignoredTagAdd" type="submit" value="{% trans "Add" %}"/>
+ <p id="hideIgnoredTagsControl">
+ <input id="hideIgnoredTagsCb" type="checkbox" {% if request.user.hide_ignored_questions %}checked="checked"{% endif %} />
+ <label id="hideIgnoredTagsLabel" for="hideIgnoredTags">{% trans "keep ingored questions hidden" %}</label>
+ <p>
+</div>
diff --git a/templates/user_email_subscriptions.html b/templates/user_email_subscriptions.html
index 8f27bd2a..c0204cbc 100644
--- a/templates/user_email_subscriptions.html
+++ b/templates/user_email_subscriptions.html
@@ -13,6 +13,9 @@
{% endif %}
<form method="POST">
{% include "edit_user_email_feeds_form.html" %}
+ <table class='form-as-table'>
+ {{tag_filter_selection_form}}
+ </table>
<div class="submit-row text-align-right">
<input type="submit" class="submit" name="save" value="{% trans "Update" %}"/>
<input type="submit" class="submit" name="stop_email" value="{% trans "Stop sending email" %}"/>
diff --git a/templates/user_stats.html b/templates/user_stats.html
index 06f1cd2b..ecc39807 100644
--- a/templates/user_stats.html
+++ b/templates/user_stats.html
@@ -97,7 +97,7 @@
<td width="180" valign="top">
{% for tag in user_tags%}
<a rel="tag"
- title="{% blocktrans with tag.name as tag_name %}see other questions tagged '{{ tag_name }}' {% endblocktrans %}"
+ title="{% blocktrans with tag.name as tag_name %}see other questions with {{view_user}}'s contributions tagged '{{ tag_name }}' {% endblocktrans %}"
href="{% url forum.views.tag tag|urlencode %}?user={{view_user.username}}">{{tag.name}}</a>
<span class="tag-number">&#215; {{ tag.user_tag_usage_count|intcomma }}</span><br/>
{% if forloop.counter|divisibleby:"10" %}
diff --git a/utils/decorators.py b/utils/decorators.py
new file mode 100644
index 00000000..e4e7acb3
--- /dev/null
+++ b/utils/decorators.py
@@ -0,0 +1,25 @@
+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')
+ return wrap
+
+def ajax_method(view_func):
+ def wrap(request,*args,**kwargs):
+ if not request.is_ajax():
+ raise Http404
+ retval = view_func(request,*args,**kwargs)
+ if isinstance(retval, HttpResponse):
+ retval.mimetype = 'application/json'
+ return retval
+ else:
+ json = simplejson.dumps(retval)
+ return HttpResponse(json,mimetype='application/json')
+ return wrap
+