From 5780495e13ec2d55bc2dee96dec372a9ea395462 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C5=81ukasz=20Rekucki?= Date: Tue, 1 Jun 2010 10:13:50 +0200 Subject: [PATCH] Basic changes, to display nicer names. --- apps/django_cas/backends.py | 2 +- apps/wiki/helpers.py | 71 +++++++++++++++++++ apps/wiki/models.py | 35 ++++++--- .../templates/wiki/document_details_base.html | 2 +- apps/wiki/templates/wiki/document_list.html | 17 +++-- .../templates/wiki/tabs/history_view.html | 1 + .../templates/wiki/tabs/summary_view.html | 11 +-- .../wiki/tabs/summary_view_item.html | 3 +- apps/wiki/urls.py | 50 ++++++++----- apps/wiki/views.py | 64 ++++++++++++----- lib/vstorage/__init__.py | 20 +++--- logging.cfg.dev | 4 +- redakcja/manage.py | 11 ++- redakcja/settings/common.py | 14 ++++ redakcja/urls.py | 5 ++ 15 files changed, 233 insertions(+), 77 deletions(-) diff --git a/apps/django_cas/backends.py b/apps/django_cas/backends.py index 328469a9..d55c9db3 100644 --- a/apps/django_cas/backends.py +++ b/apps/django_cas/backends.py @@ -53,7 +53,7 @@ def _verify_cas2(ticket, service): except: import traceback traceback.print_exc() - print "****" + print "****", url print response print "****" finally: diff --git a/apps/wiki/helpers.py b/apps/wiki/helpers.py index bc4b7602..2cdb916e 100644 --- a/apps/wiki/helpers.py +++ b/apps/wiki/helpers.py @@ -58,3 +58,74 @@ def ajax_require_permission(permission): return view(request, *args, **kwargs) return authorized_view return decorator + +import collections + +def recursive_groupby(iterable): + """ +# >>> recursive_groupby([1,2,3,4,5]) +# [1, 2, 3, 4, 5] + + >>> recursive_groupby([[1]]) + [1] + + >>> recursive_groupby([('a', 1),('a', 2), 3, ('b', 4), 5]) + ['a', [1, 2], 3, 'b', [4], 5] + + >>> recursive_groupby([('a', 'x', 1),('a', 'x', 2), ('a', 'x', 3)]) + ['a', ['x', [1, 2, 3]]] + + """ + + def _generator(iterator): + group = None + grouper = None + + for item in iterator: + if not isinstance(item, collections.Sequence): + if grouper is not None: + yield grouper + if len(group): + yield recursive_groupby(group) + group = None + grouper = None + yield item + continue + elif len(item) == 1: + if grouper is not None: + yield grouper + if len(group): + yield recursive_groupby(group) + group = None + grouper = None + yield item[0] + continue + elif not len(item): + continue + + if grouper is None: + group = [item[1:]] + grouper = item[0] + continue + + if grouper != item[0]: + if grouper is not None: + yield grouper + if len(group): + yield recursive_groupby(group) + group = None + grouper = None + group = [item[1:]] + grouper = item[0] + continue + + group.append(item[1:]) + + if grouper is not None: + yield grouper + if len(group): + yield recursive_groupby(group) + group = None + grouper = None + + return list(_generator(iterable)) diff --git a/apps/wiki/models.py b/apps/wiki/models.py index a4dc7941..d2b74605 100644 --- a/apps/wiki/models.py +++ b/apps/wiki/models.py @@ -15,6 +15,25 @@ from django.http import Http404 import logging logger = logging.getLogger("fnp.wiki") +_PCHARS_DICT = dict(zip((ord(x) for x in u"ĄĆĘŁŃÓŚŻŹąćęłńóśżź "), u"ACELNOSZZacelnoszz_")) + +# I know this is barbaric, but I didn't find a better solution ;( +def split_name(name): + parts = name.translate(_PCHARS_DICT).split('__') + logger.info("SPLIT %r -> %r", name, parts) + return parts + +def join_name(*parts, **kwargs): + name = u'__'.join(p.translate(_PCHARS_DICT) for p in parts) + logger.info("JOIN %r -> %r", parts, name) + return name + +def normalize_name(name): + """ + >>> normalize_name("gąska".decode('utf-8')) + u'gaska' + """ + return name.translate(_PCHARS_DICT).lower() STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE) @@ -37,7 +56,7 @@ class DocumentStorage(object): except DocumentNotFound: raise Http404 - def put(self, document, author, comment, parent): + def put(self, document, author, comment, parent=None): self.vstorage.save_text( title=document.name, text=document.text, @@ -47,15 +66,14 @@ class DocumentStorage(object): return document - def create_document(self, id, text, title=None): - if title is None: - title = id.title() + def create_document(self, text, name): + title = u', '.join(p.title for p in split_name(name)) if text is None: text = u'' - document = Document(self, name=id, text=text, title=title) - return self.put(document, u"", u"Document created.", None) + document = Document(self, name=name, text=text, title=title) + return self.put(document, u"", u"Document created.") def delete(self, name, author, comment): self.vstorage.delete_page(name, author, comment) @@ -109,16 +127,11 @@ class Document(object): gallery = os.path.basename(gallery) result['gallery'] = gallery - - if 'title' not in result: - result['title'] = self.name.title() - return result def info(self): return self.storage.vstorage.page_meta(self.name, self.revision) - def getstorage(): return DocumentStorage(settings.REPOSITORY_PATH) diff --git a/apps/wiki/templates/wiki/document_details_base.html b/apps/wiki/templates/wiki/document_details_base.html index 34106bb1..28567e1f 100644 --- a/apps/wiki/templates/wiki/document_details_base.html +++ b/apps/wiki/templates/wiki/document_details_base.html @@ -30,7 +30,7 @@ diff --git a/apps/wiki/templates/wiki/tabs/summary_view_item.html b/apps/wiki/templates/wiki/tabs/summary_view_item.html index ce8d5945..79c9f8f1 100644 --- a/apps/wiki/templates/wiki/tabs/summary_view_item.html +++ b/apps/wiki/templates/wiki/tabs/summary_view_item.html @@ -1,4 +1,5 @@ {% load i18n %} +{% load wiki %}
  • - {{ document_meta.title }} + {{ document.name|wiki_title }}
  • diff --git a/apps/wiki/urls.py b/apps/wiki/urls.py index bfc799f5..351ecbc8 100644 --- a/apps/wiki/urls.py +++ b/apps/wiki/urls.py @@ -1,29 +1,43 @@ +# -*- coding: utf-8 from django.conf.urls.defaults import * +from django.views.generic.simple import redirect_to +from django.conf import settings + + +PART = ur"""[ ĄĆĘŁŃÓŚŻŹąćęłńóśżź0-9\w_.-]+""" urlpatterns = patterns('wiki.views', - url(r'^$', - 'document_list', name='wiki_doclist'), + url(r'^$', redirect_to, {'url': 'catalogue/'}), + + url(r'^catalogue/$', 'document_list', name='wiki_document_list'), + url(r'^catalogue/([^/]+)/$', 'document_list'), + url(r'^catalogue/([^/]+)/([^/]+)/$', 'document_list'), + url(r'^catalogue/([^/]+)/([^/]+)/([^/]+)$', 'document_list'), + + url(r'^(?P%s)$' % PART, + 'editor', name="wiki_editor"), + + url(r'^(?P[^/]+)/readonly$', + 'editor_readonly', name="wiki_editor_readonly"), url(r'^create/(?P[^/]+)', - 'document_create_missing', name='wiki_create_missing'), + 'create_missing', name='wiki_create_missing'), + + url(r'^(?P[^/]+)/gallery$', + 'gallery', name="wiki_gallery"), - url(r'^gallery/(?P[^/]+)$', - 'document_gallery', name="wiki_gallery"), url(r'^(?P[^/]+)/history$', - 'document_history', name="wiki_history"), + 'history', name="wiki_history"), + url(r'^(?P[^/]+)/text$', - 'document_text', name="wiki_text"), - url(r'^(?P[^/]+)/publish/(?P\d+)$', - 'document_publish', name="wiki_publish"), - url(r'^(?P[^/]+)/diff$', - 'document_diff', name="wiki_diff"), - url(r'^(?P[^/]+)/tags$', - 'document_add_tag', name="wiki_add_tag"), - url(r'^(?P[^/]+)/publish$', 'document_publish'), + 'text', name="wiki_text"), + + url(r'^(?P[^/]+)/publish$', 'publish', name="wiki_publish"), + url(r'^(?P[^/]+)/publish/(?P\d+)$', 'publish', name="wiki_publish"), + + url(r'^(?P[^/]+)/diff$', 'diff', name="wiki_diff"), + url(r'^(?P[^/]+)/tags$', 'add_tag', name="wiki_add_tag"), + - url(r'^(?P[^/]+)/readonly$', - 'document_detail_readonly', name="wiki_details_readonly"), - url(r'^(?P[^/]+)$', - 'document_detail', name="wiki_details"), ) diff --git a/apps/wiki/views.py b/apps/wiki/views.py index 1539bafc..69c98365 100644 --- a/apps/wiki/views.py +++ b/apps/wiki/views.py @@ -1,47 +1,64 @@ import os +import functools +import logging +logger = logging.getLogger("fnp.wiki") from django.conf import settings from django.views.generic.simple import direct_to_template from django.views.decorators.http import require_POST, require_GET from django.core.urlresolvers import reverse -from wiki.helpers import JSONResponse, JSONFormInvalid, JSONServerError, ajax_require_permission +from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError, + ajax_require_permission, recursive_groupby) from django import http -from wiki.models import getstorage, DocumentNotFound +from wiki.models import getstorage, DocumentNotFound, normalize_name, split_name, join_name from wiki.forms import DocumentTextSaveForm, DocumentTagForm, DocumentCreateForm from datetime import datetime from django.utils.encoding import smart_unicode from django.utils.translation import ugettext_lazy as _ -import wlapi # # Quick hack around caching problems, TODO: use ETags # from django.views.decorators.cache import never_cache -import logging -logger = logging.getLogger("fnp.peanut.api") - +import wlapi import nice_diff import operator MAX_LAST_DOCS = 10 +def normalized_name(view): + + @functools.wraps(view) + def decorated(request, name, *args): + normalized = normalize_name(name) + logger.debug('View check %r -> %r', name, normalized) + + if normalized != name: + return http.HttpResponseRedirect( + reverse('wiki_' + view.__name__, kwargs={'name': normalized})) + + return view(request, name, *args) + + return decorated + + @never_cache -def document_list(request, template_name='wiki/document_list.html'): - # TODO: find a way to cache "Storage All" - return direct_to_template(request, template_name, extra_context={ - 'document_list': getstorage().all(), +def document_list(request): + return direct_to_template(request, 'wiki/document_list.html', extra_context={ + 'docs': getstorage().all(), 'last_docs': sorted(request.session.get("wiki_last_docs", {}).items(), key=operator.itemgetter(1), reverse=True), }) @never_cache -def document_detail(request, name, template_name='wiki/document_details.html'): +@normalized_name +def editor(request, name, template_name='wiki/document_details.html'): storage = getstorage() try: @@ -71,7 +88,9 @@ def document_detail(request, name, template_name='wiki/document_details.html'): @require_GET -def document_detail_readonly(request, name, template_name='wiki/document_details_readonly.html'): +@normalized_name +def editor_readonly(request, name, template_name='wiki/document_details_readonly.html'): + name = normalize_name(name) storage = getstorage() try: @@ -97,7 +116,8 @@ def document_detail_readonly(request, name, template_name='wiki/document_details }) -def document_create_missing(request, name): +@normalized_name +def create_missing(request, name): storage = getstorage() if request.method == "POST": @@ -122,7 +142,8 @@ def document_create_missing(request, name): @never_cache -def document_text(request, name): +@normalized_name +def text(request, name): storage = getstorage() if request.method == 'POST': @@ -174,7 +195,7 @@ def document_text(request, name): @never_cache -def document_gallery(request, directory): +def gallery(request, directory): try: base_url = ''.join(( smart_unicode(settings.MEDIA_URL), @@ -201,7 +222,8 @@ def document_gallery(request, directory): @never_cache -def document_diff(request, name): +@normalized_name +def diff(request, name): storage = getstorage() revA = int(request.GET.get('from', 0)) @@ -221,7 +243,8 @@ def document_diff(request, name): @never_cache -def document_history(request, name): +@normalized_name +def history(request, name): storage = getstorage() # TODO: pagination @@ -232,7 +255,8 @@ def document_history(request, name): @require_POST @ajax_require_permission('wiki.can_change_tags') -def document_add_tag(request, name): +def add_tag(request, name): + name = normalize_name(name) storage = getstorage() form = DocumentTagForm(request.POST, prefix="addtag") @@ -248,7 +272,9 @@ def document_add_tag(request, name): @require_POST @ajax_require_permission('wiki.can_publish') -def document_publish(request, name): +def publish(request, name): + name = normalize_name(name) + storage = getstorage() document = storage.get_by_tag(name, "ready_to_publish") diff --git a/lib/vstorage/__init__.py b/lib/vstorage/__init__.py index b4acaf3a..aeb7338a 100644 --- a/lib/vstorage/__init__.py +++ b/lib/vstorage/__init__.py @@ -27,18 +27,18 @@ def urlquote(url, safe='/'): """Quotes URL >>> urlquote(u'Za\u017c\xf3\u0142\u0107 g\u0119\u015bl\u0105 ja\u017a\u0144') - 'Za%C5%BC%C3%B3%C5%82%C4%87_g%C4%99%C5%9Bl%C4%85_ja%C5%BA%C5%84' + 'Za%C5%BC%C3%B3%C5%82%C4%87%20g%C4%99%C5%9Bl%C4%85%20ja%C5%BA%C5%84' """ - return urllib.quote(url.replace(' ', '_').encode('utf-8', 'ignore'), safe) + return urllib.quote(url.encode('utf-8', 'ignore'), safe) def urlunquote(url): """Unqotes URL # >>> urlunquote('Za%C5%BC%C3%B3%C5%82%C4%87_g%C4%99%C5%9Bl%C4%85_ja%C5%BA%C5%84') - # u'Za\u017c\xf3\u0142\u0107 g\u0119\u015bl\u0105 ja\u017a\u0144' + # u'Za\u017c\xf3\u0142\u0107_g\u0119\u015bl\u0105 ja\u017a\u0144' """ - return unicode(urllib.unquote(url), 'utf-8', 'ignore').replace('_', ' ') + return unicode(urllib.unquote(url), 'utf-8', 'ignore') def find_repo_path(path): @@ -151,12 +151,12 @@ class VersionedStorage(object): def _file_path(self, title): return os.path.join(self.path, urlquote(title, safe='')) - def _title_to_file(self, title): - return os.path.join(self.repo_prefix, urlquote(title, safe='')) + def _title_to_file(self, title, type=".xml"): + return os.path.join(self.repo_prefix, urlquote(title, safe='')) + type def _file_to_title(self, filename): assert filename.startswith(self.repo_prefix) - name = filename[len(self.repo_prefix):].strip('/') + name = filename[len(self.repo_prefix):].strip('/').split('.', 1)[0] return urlunquote(name) def __contains__(self, title): @@ -411,10 +411,12 @@ class VersionedStorage(object): rev = -1 yield title, rev, date, author, comment - def all_pages(self): + def all_pages(self, type=''): tip = self.repo['tip'] """Iterate over the titles of all pages in the wiki.""" - return [urlunquote(filename) for filename in tip] + return [self._file_to_title(filename) for filename in tip + if not filename.startswith('.') + and filename.endswith(type) ] def changed_since(self, rev): """Return all pages that changed since specified repository revision.""" diff --git a/logging.cfg.dev b/logging.cfg.dev index 9bfa56c2..23a78331 100644 --- a/logging.cfg.dev +++ b/logging.cfg.dev @@ -23,7 +23,7 @@ datefmt= class=logging.Formatter [handler_console] -class=StreamHandler +class=FileHandler level=DEBUG formatter=default -args=(sys.stderr, ) \ No newline at end of file +args=('redakcja.dev.log', ) diff --git a/redakcja/manage.py b/redakcja/manage.py index 1fc25dde..4c118422 100755 --- a/redakcja/manage.py +++ b/redakcja/manage.py @@ -9,10 +9,15 @@ except ImportError: if __name__ == "__main__": # Append lib and apps directories to PYTHONPATH - from os import path + import os import sys - PROJECT_ROOT = path.realpath(path.dirname(__file__)) - sys.path += [PROJECT_ROOT + '/../apps', PROJECT_ROOT + '/../lib'] + PROJECT_ROOT = os.path.realpath(os.path.dirname(__file__)) + sys.path += [os.path.realpath(os.path.join(*x)) for x in ( + (PROJECT_ROOT, '..', 'apps'), + (PROJECT_ROOT, '..', 'lib') + )] + + execute_manager(settings) diff --git a/redakcja/settings/common.py b/redakcja/settings/common.py index 0a3b3765..379f5b59 100644 --- a/redakcja/settings/common.py +++ b/redakcja/settings/common.py @@ -140,3 +140,17 @@ WL_API_CONFIG = { } # Import localsettings file, which may override settings defined here + +try: + import logging + if os.path.isfile(LOGGING_CONFIG_FILE): + import logging.config + logging.config.fileConfig(LOGGING_CONFIG_FILE) + else: + import sys + logging.basicConfig(stream=sys.stderr) +except NameError: + print "No logging" + pass +except ImportError as exc: + raise diff --git a/redakcja/urls.py b/redakcja/urls.py index f8538fa8..191bcc2a 100644 --- a/redakcja/urls.py +++ b/redakcja/urls.py @@ -4,6 +4,8 @@ from django.conf.urls.defaults import * from django.contrib import admin from django.conf import settings +import wiki.urls + admin.autodiscover() urlpatterns = patterns('', @@ -26,4 +28,7 @@ urlpatterns = patterns('', {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), url(r'^%s(?P.+)$' % settings.STATIC_URL[1:], 'django.views.static.serve', {'document_root': settings.STATIC_ROOT, 'show_indexes': True}), + (r'^documents/', include(wiki.urls)), + url(r'^$', 'django.views.generic.simple.redirect_to', {'url': '/documents/'}), + ) -- 2.20.1