From e14bec1444b50a351b0d228d25f6d6cf56a8151c Mon Sep 17 00:00:00 2001 From: hernani Date: Wed, 7 Jul 2010 01:33:19 +0000 Subject: [PATCH] Adds better permalinks to answers, computing the position of an answer in the list. git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@494 0cfe37f9-358a-4d5e-be75-b63607b5c754 --- forum/models/answer.py | 2 +- .../default/templates/node/post_controls.html | 4 +- forum/templatetags/node_tags.py | 2 +- forum/urls.py | 6 +- forum/utils/pagination.py | 124 +++++++++++------- forum/views/readers.py | 59 +++++++-- 6 files changed, 138 insertions(+), 59 deletions(-) diff --git a/forum/models/answer.py b/forum/models/answer.py index e5f166b..04735a4 100644 --- a/forum/models/answer.py +++ b/forum/models/answer.py @@ -16,7 +16,7 @@ class Answer(Node): return self.question.headline def get_absolute_url(self): - return '%s#%s' % (self.question.get_absolute_url(), self.id) + return '%s/%s' % (self.question.get_absolute_url(), self.id) class AnswerRevision(NodeRevision): diff --git a/forum/skins/default/templates/node/post_controls.html b/forum/skins/default/templates/node/post_controls.html index e756fff..bbe1e22 100644 --- a/forum/skins/default/templates/node/post_controls.html +++ b/forum/skins/default/templates/node/post_controls.html @@ -2,7 +2,7 @@ {% spaceless %} {% for control in controls %} - {{ control.text }} + {{ control.text }} {% ifnotequal controls|last control %} | @@ -14,7 +14,7 @@ {% trans "more" %} ▼ diff --git a/forum/templatetags/node_tags.py b/forum/templatetags/node_tags.py index a840a2c..3124f3a 100644 --- a/forum/templatetags/node_tags.py +++ b/forum/templatetags/node_tags.py @@ -65,7 +65,7 @@ def post_controls(post, user): post_type = post.node_type if post_type == "answer": - controls.append(post_control(_('permanent link'), '#%d' % post.id, title=_("answer permanent link"))) + controls.append(post_control(_('permanent link'), post.get_absolute_url(), title=_("answer permanent link"))) edit_url = reverse('edit_' + post_type, kwargs={'id': post.id}) if user.can_edit_post(post): diff --git a/forum/urls.py b/forum/urls.py index 79f7c11..92c0d6b 100644 --- a/forum/urls.py +++ b/forum/urls.py @@ -93,8 +93,10 @@ urlpatterns += patterns('', url(r'^%s(?P\d+)/(?P[\w-]*)$' % _('question/'), 'django.views.generic.simple.redirect_to', {'url': '/questions/%(id)s/%(slug)s'}), - url(r'^%s(?P\d+)/(?P[\w-]*)$' % _('questions/'), app.readers.question, name='question' - ), + url(r'^%s(?P\d+)/(?P[\w-]*)$' % _('questions/'), app.readers.question, name='question'), + url(r'^%s(?P\d+)/(?P[\w-]*)/(?P\d+)$' % _('questions/'), app.readers.question), + + url(r'^%s$' % _('tags/'), app.readers.tags, name='tags'), url(r'^%s(?P.*)/$' % _('tags/'), app.readers.tag, name='tag_questions'), diff --git a/forum/utils/pagination.py b/forum/utils/pagination.py index 393fac0..afadb28 100644 --- a/forum/utils/pagination.py +++ b/forum/utils/pagination.py @@ -17,6 +17,14 @@ class SimpleSort(object): def apply(self, objects): return objects.order_by(self.order_by) +class DummySort(object): + def __init__(self, label, description=''): + self.label = label + self.description = description + + def apply(self, objects): + return objects + class PaginatorContext(object): visible_page_range = 5 @@ -24,7 +32,8 @@ class PaginatorContext(object): base_path = None - def __init__(self, id, sort_methods=None, default_sort=None, pagesizes=None, default_pagesize=None): + def __init__(self, id, sort_methods=None, default_sort=None, force_sort = None, sticky_sort=False, + pagesizes=None, default_pagesize=None): self.id = id if sort_methods: self.has_sort = True @@ -49,9 +58,72 @@ class PaginatorContext(object): else: self.has_pagesize = False - self.force_sort = None - self.sticky_sort = False + self.force_sort = force_sort + self.sticky_sort = sticky_sort + + def session_preferences(self, request): + return request.session.get('paginator_%s' % self.id, {}) + + def pagesize(self, request, session_prefs=None): + if not session_prefs: + session_prefs = self.session_preferences(request) + + + if self.has_pagesize: + if request.GET.get(labels.PAGESIZE, None): + try: + pagesize = int(request.GET[labels.PAGESIZE]) + except ValueError: + logging.error('Found invalid page size "%s", loading %s, refered by %s' % ( + request.GET.get(labels.PAGESIZE, ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') + )) + raise Http404() + + session_prefs[labels.PAGESIZE] = pagesize + else: + pagesize = session_prefs.get(labels.PAGESIZE, self.default_pagesize) + + if not pagesize in self.pagesizes: + pagesize = self.default_pagesize + else: + pagesize = 30 + + return pagesize + + def page(self, request): + try: + return int(request.GET.get(labels.PAGE, 1)) + except ValueError: + logging.error('Found invalid page number "%s", loading %s, refered by %s' % ( + request.GET.get(labels.PAGE, ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') + )) + raise Http404() + + def sort(self, request, session_prefs=None): + if not session_prefs: + session_prefs = self.session_preferences(request) + + sort = None + if self.has_sort: + if request.GET.get(labels.SORT, None): + sort = request.GET[labels.SORT] + if self.sticky_sort or session_prefs.get('sticky_sort', False): + session_prefs[labels.SORT] = sort + else: + sort = self.force_sort or session_prefs.get(labels.SORT, self.default_sort) + + if not sort in self.sort_methods: + sort = self.default_sort + + return sort + def sorted(self, objects, request, session_prefs=None): + sort = self.sort(request, session_prefs) + + if sort: + objects = self.sort_methods[sort].apply(objects) + + return sort, objects class labels(object): @@ -64,49 +136,13 @@ page_sizes_template = template.loader.get_template('paginator/page_sizes.html') sort_tabs_template = template.loader.get_template('paginator/sort_tabs.html') def paginated(request, list_name, context, tpl_context): - session_prefs = request.session.get('paginator_%s' % context.id, {}) - objects = tpl_context[list_name] - - if context.has_pagesize: - if request.GET.get(labels.PAGESIZE, None): - try: - pagesize = int(request.GET[labels.PAGESIZE]) - except ValueError: - logging.error('Found invalid page size "%s", loading %s, refered by %s' % ( - request.GET.get(labels.PAGESIZE, ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') - )) - raise Http404() - - session_prefs[labels.PAGESIZE] = pagesize - else: - pagesize = session_prefs.get(labels.PAGESIZE, context.default_pagesize) - - if not pagesize in context.pagesizes: - pagesize = context.default_pagesize - else: - pagesize = 30 - - try: - page = int(request.GET.get(labels.PAGE, 1)) - except ValueError: - logging.error('Found invalid page number "%s", loading %s, refered by %s' % ( - request.GET.get(labels.PAGE, ''), request.path, request.META.get('HTTP_REFERER', 'UNKNOWN') - )) - raise Http404() - - sort = None - if context.has_sort: - if request.GET.get(labels.SORT, None): - sort = request.GET[labels.SORT] - if context.sticky_sort or session_prefs.get('sticky_sort', False): - session_prefs[labels.SORT] = sort - else: - sort = context.force_sort or session_prefs.get(labels.SORT, context.default_sort) + session_prefs = context.session_preferences(request) - if not sort in context.sort_methods: - sort = context.default_sort + objects = tpl_context[list_name] - objects = context.sort_methods[sort].apply(objects) + pagesize = context.pagesize(request, session_prefs) + page = context.page(request) + sort, objects = context.sorted(objects, request, session_prefs) paginator = Paginator(objects, pagesize) diff --git a/forum/views/readers.py b/forum/views/readers.py index eed8fd5..7bfd75a 100644 --- a/forum/views/readers.py +++ b/forum/views/readers.py @@ -52,15 +52,20 @@ class QuestionListPaginatorContext(pagination.PaginatorContext): (_('mostvoted'), pagination.SimpleSort(_('most voted'), '-score', _("most voted questions"))), ), pagesizes=(15, 30, 50)) +class AnswerSort(pagination.SimpleSort): + def apply(self, objects): + if self.label == _('votes'): + return objects.order_by('-marked', self.order_by, 'added_at') + else: + return objects.order_by('-marked', self.order_by) + class AnswerPaginatorContext(pagination.PaginatorContext): def __init__(self): super (AnswerPaginatorContext, self).__init__('ANSWER_LIST', sort_methods=( - (_('oldest'), pagination.SimpleSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))), - (_('latest'), pagination.SimpleSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))), - (_('votes'), pagination.SimpleSort(_('popular answers'), '-score', _("most voted answers will be shown first"))), - ), default_sort=_('votes'), pagesizes=(5, 10, 20)) - - self.sticky_sort = True + (_('oldest'), AnswerSort(_('oldest answers'), 'added_at', _("oldest answers will be shown first"))), + (_('newest'), AnswerSort(_('newest answers'), '-added_at', _("newest answers will be shown first"))), + (_('votes'), AnswerSort(_('popular answers'), '-score', _("most voted answers will be shown first"))), + ), default_sort=_('votes'), sticky_sort = True, pagesizes=(5, 10, 20)) def feed(request): @@ -277,27 +282,63 @@ def match_question_slug(slug): return None +def answer_redirect(request, answer): + pc = AnswerPaginatorContext() + + sort = pc.sort(request) + + if sort == _('oldest'): + filter = Q(added_at__lt=answer.added_at) + elif sort == _('newest'): + filter = Q(added_at__gt=answer.added_at) + elif sort == _('votes'): + filter = Q(score__gt=answer.score) | Q(score=answer.score, added_at__lt=answer.added_at) + else: + raise Http404() + + count = answer.question.answers.filter(Q(marked=True) | filter).count() + pagesize = pc.pagesize(request) + + page = count / pagesize + + if count % pagesize: + page += 1 + + return HttpResponsePermanentRedirect("%s?%s=%s#%s" % ( + answer.question.get_absolute_url(), _('page'), page, answer.id)) + @decorators.render("question.html", 'questions', tabbed=False) -def question(request, id, slug): +def question(request, id, slug, answer=None): try: question = Question.objects.get(id=id) except: if slug: question = match_question_slug(slug) if question is not None: - return HttpResponsePermanentRedirect(question.get_absolute_url()) + return HttpResponseRedirect(question.get_absolute_url()) raise Http404() if question.nis.deleted and not request.user.can_view_deleted_post(question): raise Http404 + if answer: + answer = get_object_or_404(Answer, id=answer) + + if (question.nis.deleted and not request.user.can_view_deleted_post(question)) or answer.question != question: + raise Http404 + + if answer.marked: + return HttpResponsePermanentRedirect(question.get_absolute_url()) + + return answer_redirect(request, answer) + if request.POST: answer_form = AnswerForm(question, request.POST) else: answer_form = AnswerForm(question) - answers = request.user.get_visible_answers(question).order_by("-marked") + answers = request.user.get_visible_answers(question) update_question_view_times(request, question) -- 2.39.5