class Meta:
model = Book
- exclude = ['gallery', 'parent', 'parent_number']
- prepopulated_fields = {'slug': ['title']}
+ exclude = ['parent', 'parent_number']
+
+ def __init__(self, *args, **kwargs):
+ super(DocumentCreateForm, self).__init__(*args, **kwargs)
+ self.fields['slug'].widget.attrs={'class': 'autoslug'}
+ self.fields['gallery'].widget.attrs={'class': 'autoslug'}
+ self.fields['title'].widget.attrs={'class': 'autoslug-source'}
def clean(self):
super(DocumentCreateForm, self).clean()
try:
self.cleaned_data['text'] = file.read().decode('utf-8')
except UnicodeDecodeError:
- raise forms.ValidationError("Text file must be UTF-8 encoded.")
+ raise forms.ValidationError(_("Text file must be UTF-8 encoded."))
if not self.cleaned_data["text"]:
- raise forms.ValidationError("You must either enter text or upload a file")
+ self._errors["file"] = self.error_class([_("You must either enter text or upload a file")])
return self.cleaned_data
msgstr ""
"Project-Id-Version: Platforma Redakcyjna\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-10-21 15:53+0200\n"
-"PO-Revision-Date: 2011-10-21 15:53+0100\n"
+"POT-Creation-Date: 2011-10-24 13:25+0200\n"
+"PO-Revision-Date: 2011-10-24 13:28+0100\n"
"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
"Language: pl\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
-#: forms.py:46
+#: forms.py:39
+#, fuzzy
+msgid "Text file must be UTF-8 encoded."
+msgstr "Plik powinien mieć kodowanie UTF-8."
+
+#: forms.py:42
+msgid "You must either enter text or upload a file"
+msgstr "Proszę wpisać tekst albo wybrać plik do załadowania"
+
+#: forms.py:51
msgid "ZIP file"
msgstr "Plik ZIP"
-#: forms.py:47
+#: forms.py:52
msgid "Directories are documents in chunks"
msgstr "Katalogi zawierają dokumenty w częściach"
-#: forms.py:71
+#: forms.py:76
msgid "Assigned to"
msgstr "Przypisane do"
-#: forms.py:91
-#: forms.py:105
+#: forms.py:96
+#: forms.py:110
msgid "Chunk with this slug already exists"
msgstr "Część z tym slugiem już istnieje"
-#: forms.py:114
+#: forms.py:119
msgid "Append to"
msgstr "Dołącz do"
-#: views.py:148
+#: views.py:149
#, python-format
msgid "Slug already used for %s"
msgstr "Slug taki sam jak dla pliku %s"
-#: views.py:150
+#: views.py:151
msgid "Slug already used in repository."
msgstr "Dokument o tym slugu już istnieje w repozytorium."
-#: views.py:156
+#: views.py:157
msgid "File should be UTF-8 encoded."
msgstr "Plik powinien mieć kodowanie UTF-8."
#: models/book.py:25
-#: models/chunk.py:24
+#: models/chunk.py:23
msgid "title"
msgstr "tytuł"
#: models/book.py:26
-#: models/chunk.py:25
+#: models/chunk.py:24
msgid "slug"
msgstr "slug"
#: models/book.py:27
+msgid "public"
+msgstr "publiczna"
+
+#: models/book.py:28
msgid "scan gallery name"
msgstr "nazwa galerii skanów"
-#: models/book.py:30
+#: models/book.py:31
msgid "parent"
msgstr "rodzic"
-#: models/book.py:31
+#: models/book.py:32
msgid "parent number"
msgstr "numeracja rodzica"
-#: models/book.py:48
-#: models/chunk.py:22
+#: models/book.py:46
+#: models/chunk.py:21
#: models/publish_log.py:17
msgid "book"
msgstr "książka"
-#: models/book.py:49
+#: models/book.py:47
msgid "books"
msgstr "książki"
-#: models/book.py:197
+#: models/book.py:198
msgid "No chunks in the book."
msgstr "Książka nie ma części."
-#: models/book.py:201
+#: models/book.py:202
msgid "Not all chunks have publishable revisions."
msgstr "Niektóre części nie są gotowe do publikacji."
-#: models/book.py:207
+#: models/book.py:208
msgid "Invalid XML"
msgstr "Nieprawidłowy XML"
-#: models/book.py:209
+#: models/book.py:210
msgid "No Dublin Core found."
msgstr "Brak sekcji Dublin Core."
-#: models/book.py:211
+#: models/book.py:212
msgid "Invalid Dublin Core"
msgstr "Nieprawidłowy Dublin Core"
-#: models/book.py:214
+#: models/book.py:215
msgid "rdf:about is not"
msgstr "rdf:about jest różny od"
-#: models/chunk.py:23
+#: models/chunk.py:22
msgid "number"
msgstr "numer"
-#: models/chunk.py:26
+#: models/chunk.py:25
msgid "gallery start"
msgstr "początek galerii"
-#: models/chunk.py:41
+#: models/chunk.py:40
msgid "chunk"
msgstr "część"
-#: models/chunk.py:42
+#: models/chunk.py:41
msgid "chunks"
msgstr "części"
msgid "Book"
msgstr "Książka"
-#: templates/catalogue/document_create_missing.html:9
-msgid "Create document"
-msgstr "Utwórz dokument"
+#: templates/catalogue/document_create_missing.html:5
+msgid "Create a new book"
+msgstr "Utwórz nową książkę"
+
+#: templates/catalogue/document_create_missing.html:11
+msgid "Create book"
+msgstr "Utwórz książkę"
#: templates/catalogue/document_upload.html:8
msgid "Bulk documents upload"
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'Book.public'
+ db.add_column('catalogue_book', 'public', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'Book.public'
+ db.delete_column('catalogue_book', 'public')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'catalogue.book': {
+ 'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+ '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}),
+ 'parent_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'catalogue.bookpublishrecord': {
+ 'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'},
+ 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'catalogue.chunk': {
+ 'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+ '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+ '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}),
+ 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'gallery_start': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'number': ('django.db.models.fields.IntegerField', [], {}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
+ },
+ 'catalogue.chunkchange': {
+ 'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
+ 'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}),
+ 'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}),
+ 'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"})
+ },
+ 'catalogue.chunkpublishrecord': {
+ 'Meta': {'object_name': 'ChunkPublishRecord'},
+ 'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}),
+ 'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ 'catalogue.chunktag': {
+ 'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'ordering': ('django.db.models.fields.IntegerField', [], {}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['catalogue']
title = models.CharField(_('title'), max_length=255, db_index=True)
slug = models.SlugField(_('slug'), max_length=128, unique=True, db_index=True)
+ public = models.BooleanField(_('public'), default=True, db_index=True)
gallery = models.CharField(_('scan gallery name'), max_length=255, blank=True)
#wl_slug = models.CharField(_('title'), max_length=255, null=True, db_index=True, editable=False)
_new_publishable = models.NullBooleanField(editable=False)
_published = models.NullBooleanField(editable=False)
- # Managers
- objects = models.Manager()
-
class NoTextError(BaseException):
pass
# Creating & manipulating
# =======================
+ def accessible(self, request):
+ return self.public or request.user.is_authenticated()
+
@classmethod
def create(cls, creator, text, *args, **kwargs):
b = cls.objects.create(*args, **kwargs)
from catalogue.helpers import cached_in_field
from catalogue.managers import VisibleManager
from catalogue.tasks import refresh_instance
-from catalogue.xml_tools import GradedText
from dvcs import models as dvcs_models
self.changed
self.hidden
self.short_html
-
- def graded(self, master=None):
- return GradedText(self.materialize(), master)
from celery.task import task
+from django.utils import translation
+from django.conf import settings
@task
-def refresh_by_pk(cls, pk):
- cls._default_manager.get(pk=pk).refresh()
-
+def _refresh_by_pk(cls, pk, language=None):
+ prev_language = translation.get_language()
+ language and translation.activate(language)
+ try:
+ cls._default_manager.get(pk=pk).refresh()
+ finally:
+ translation.activate(prev_language)
-def refresh_instance(instance):
- refresh_by_pk.delay(type(instance), instance.pk)
+if settings.USE_CELERY:
+ def refresh_instance(instance):
+ _refresh_by_pk.delay(type(instance), instance.pk, translation.get_language())
+else:
+ def refresh_instance(instance):
+ instance.refresh()
@task
-def publishable_error(book):
+def _publishable_error(book, language=None):
+ prev_language = translation.get_language()
+ language and translation.activate(language)
try:
- book.assert_publishable()
+ return book.assert_publishable()
except AssertionError, e:
return e
else:
return None
+ finally:
+ translation.activate(prev_language)
+
+if settings.USE_CELERY:
+ def publishable_error(book):
+ task = _publishable_error.delay(book, translation.get_language())
+ return task.wait()
+else:
+ def publishable_error(book):
+ return _publishable_error(book)
{% extends "catalogue/base.html" %}
{% load i18n %}
-{% block leftcolumn %}
- <form enctype="multipart/form-data" method="POST" action="">
- {% csrf_token %}
- {{ form.as_p }}
-
- <p><button type="submit">{% trans "Create document" %}</button></p>
- </form>
-{% endblock leftcolumn %}
+{% block content %}
+ <h1>{% trans "Create a new book" %}</h1>
-{% block rightcolumn %}
-{% endblock rightcolumn %}
+ <form enctype="multipart/form-data" method="POST">
+ {% csrf_token %}
+ <table class='editable'>
+ {{ form.as_table}}
+ <tr><td></td><td><button type="submit">{% trans "Create book" %}</button></td></tr>
+ </table>
+ </form>
+{% endblock content %}
chunks = chunks.order_by('book__title', 'book', 'number')
+ if not request.user.is_authenticated():
+ chunks = chunks.filter(book__public=True)
+
state = arg_or_GET('status')
if state in _states_dict:
chunks = chunks.filter(_states_dict[state])
from django.core.urlresolvers import reverse
from django.db.models import Count, Q
from django import http
-from django.http import Http404
+from django.http import Http404, HttpResponseForbidden
from django.shortcuts import get_object_or_404, render
from django.utils.encoding import iri_to_uri
from django.utils.http import urlquote_plus
from catalogue.helpers import active_tab
from catalogue.models import Book, Chunk, BookPublishRecord, ChunkPublishRecord
from catalogue.tasks import publishable_error
-from catalogue import xml_tools
#
# Quick hack around caching problems, TODO: use ETags
creator=creator,
slug=form.cleaned_data['slug'],
title=form.cleaned_data['title'],
+ gallery=form.cleaned_data['gallery'],
)
- return http.HttpResponseRedirect(reverse("wiki_editor", args=[book.slug]))
+ return http.HttpResponseRedirect(reverse("catalogue_book", args=[book.slug]))
else:
form = forms.DocumentCreateForm(initial={
"slug": slug,
"title": slug.replace('-', ' ').title(),
+ "gallery": slug,
})
return direct_to_template(request, "catalogue/document_create_missing.html", extra_context={
@never_cache
def book_xml(request, slug):
- xml = get_object_or_404(Book, slug=slug).materialize()
+ book = get_object_or_404(Book, slug=slug)
+ if not book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+ xml = book.materialize()
response = http.HttpResponse(xml, content_type='application/xml', mimetype='application/wl+xml')
response['Content-Disposition'] = 'attachment; filename=%s.xml' % slug
@never_cache
def book_txt(request, slug):
- xml = get_object_or_404(Book, slug=slug).materialize()
+ book = get_object_or_404(Book, slug=slug)
+ if not book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+ xml = book.materialize()
output = StringIO()
# errors?
librarian.text.transform(StringIO(xml), output)
@never_cache
def book_html(request, slug):
- xml = get_object_or_404(Book, slug=slug).materialize()
+ book = get_object_or_404(Book, slug=slug)
+ if not book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+ xml = book.materialize()
output = StringIO()
# errors?
librarian.html.transform(StringIO(xml), output, parse_dublincore=False,
response = http.HttpResponse(html, content_type='text/html', mimetype='text/html')
return response
-
@never_cache
def revision(request, slug, chunk=None):
try:
doc = Chunk.get(slug, chunk)
except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
raise Http404
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
return http.HttpResponse(str(doc.revision()))
def book(request, slug):
book = get_object_or_404(Book, slug=slug)
+ if not book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
if request.user.has_perm('catalogue.change_book'):
if request.method == "POST":
form = forms.ReadonlyBookForm(instance=book)
editable = False
- task = publishable_error.delay(book)
- publish_error = task.wait()
+ publish_error = publishable_error(book)
publishable = publish_error is None
return direct_to_template(request, "catalogue/book_detail.html", extra_context={
doc = Chunk.get(slug, chunk)
except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
raise Http404
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
if request.method == "POST":
form = forms.ChunkAddForm(request.POST, instance=doc)
doc = Chunk.get(slug, chunk)
except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
raise Http404
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+
if request.method == "POST":
form = forms.ChunkForm(request.POST, instance=doc)
if form.is_valid():
@permission_required('catalogue.change_book')
def book_append(request, slug):
book = get_object_or_404(Book, slug=slug)
+ if not book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+
if request.method == "POST":
form = forms.BookAppendForm(book, request.POST)
if form.is_valid():
@login_required
def publish(request, slug):
book = get_object_or_404(Book, slug=slug)
+ if not book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+
try:
book.publish(request.user)
except NotAuthorizedError:
# -*- coding: utf-8 -*-
from copy import deepcopy
-from functools import wraps
import re
from lxml import etree
pass
-def obj_memoized(f):
- """
- A decorator that caches return value of object methods.
- The cache is kept with the object, in a _obj_memoized property.
- """
- @wraps(f)
- def wrapper(self, *args, **kwargs):
- if not hasattr(self, '_obj_memoized'):
- self._obj_memoized = {}
- key = (f.__name__,) + args + tuple(sorted(kwargs.iteritems()))
- try:
- return self._obj_memoized[key]
- except TypeError:
- return f(self, *args, **kwargs)
- except KeyError:
- self._obj_memoized[key] = f(self, *args, **kwargs)
- return self._obj_memoized[key]
- return wrapper
-
-
-class GradedText(object):
- _edoc = None
-
- ROOT = 'utwor'
- RDF = '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF'
-
- def __init__(self, text):
- self._text = text
-
- @obj_memoized
- def is_xml(self):
- """
- Determines if it's a well-formed XML.
-
- >>> GradedText("<a/>").is_xml()
- True
- >>> GradedText("<a>").is_xml()
- False
- """
- try:
- self._edoc = etree.fromstring(self._text)
- except etree.XMLSyntaxError:
- return False
- return True
-
- @obj_memoized
- def is_wl(self):
- """
- Determines if it's an XML with a <utwor> and a master tag.
-
- >>> GradedText("<utwor><powiesc></powiesc></utwor>").is_wl()
- True
- >>> GradedText("<a></a>").is_wl()
- False
- """
- if self.is_xml():
- e = self._edoc
- # FIXME: there could be comments
- ret = e.tag == self.ROOT and (
- len(e) == 1 and e[0].tag in MASTERS or
- len(e) == 2 and e[0].tag == self.RDF
- and e[1].tag in MASTERS)
- if ret:
- self._master = e[-1].tag
- del self._edoc
- return ret
- else:
- return False
-
- @obj_memoized
- def is_broken_wl(self):
- """
- Determines if it at least looks like broken WL file
- and not just some untagged text.
-
- >>> GradedText("<utwor><</utwor>").is_broken_wl()
- True
- >>> GradedText("some text").is_broken_wl()
- False
- """
- if self.is_wl():
- return True
- text = self._text.strip()
- return text.startswith('<utwor>') and text.endswith('</utwor>')
-
- def master(self):
- """
- Gets the master tag.
-
- >>> GradedText("<utwor><powiesc></powiesc></utwor>").master()
- 'powiesc'
- """
- assert self.is_wl()
- return self._master
-
- @obj_memoized
- def has_trim_begin(self):
- return RE_TRIM_BEGIN.search(self._text)
-
- @obj_memoized
- def has_trim_end(self):
- return RE_TRIM_END.search(self._text)
-
-
def _trim(text, trim_begin=True, trim_end=True):
"""
Cut off everything before RE_TRIM_BEGIN and after RE_TRIM_END, so
return "".join(texts)
-def change_master(text, master):
- """
- Changes the master tag in a WL document.
- """
- e = etree.fromstring(text)
- e[-1].tag = master
- return unicode(etree.tostring(e, encoding="utf-8"), 'utf-8')
-
-
-def basic_structure(text, master):
- e = etree.fromstring('''<utwor>
-<master>
-<!--%s--><!--%s-->
-</master>
-</utwor>''' % (TRIM_BEGIN, TRIM_END))
- e[0].tag = master
- e[0][0].tail = "\n"*3 + text + "\n"*3
- return unicode(etree.tostring(e, encoding="utf-8"), 'utf-8')
-
-
def add_trim_begin(text):
trim_tag = etree.Comment(TRIM_BEGIN)
e = etree.fromstring(text)
from django.conf import settings
from django.core.urlresolvers import reverse
from django import http
-from django.http import Http404
+from django.http import Http404, HttpResponseForbidden
from django.middleware.gzip import GZipMiddleware
from django.utils.decorators import decorator_from_middleware
from django.utils.encoding import smart_unicode
return http.HttpResponseRedirect(reverse("catalogue_create_missing", args=[slug]))
else:
raise Http404
+ if not chunk.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
access_time = datetime.now()
last_books = request.session.get("wiki_last_books", {})
revision = request.GET['revision']
except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist, KeyError):
raise Http404
+ if not chunk.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
access_time = datetime.now()
last_books = request.session.get("wiki_last_books", {})
@decorator_from_middleware(GZipMiddleware)
def text(request, chunk_id):
doc = get_object_or_404(Chunk, pk=chunk_id)
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
if request.method == 'POST':
form = forms.DocumentTextSaveForm(request.POST, user=request.user, prefix="textsave")
form = forms.DocumentTextRevertForm(request.POST, prefix="textrevert")
if form.is_valid():
doc = get_object_or_404(Chunk, pk=chunk_id)
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
revision = form.cleaned_data['revision']
images = [map_to_url(f) for f in map(smart_unicode, os.listdir(base_dir)) if is_image(f)]
images.sort()
+
+ if not request.user.is_authenticated():
+ return HttpResponseForbidden("Not authorized.")
+
return JSONResponse(images)
except (IndexError, OSError):
logger.exception("Unable to fetch gallery")
revB = None
doc = get_object_or_404(Chunk, pk=chunk_id)
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+
# allow diff from the beginning
if revA:
docA = doc.at_revision(revA).materialize()
@never_cache
def revision(request, chunk_id):
doc = get_object_or_404(Chunk, pk=chunk_id)
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
return http.HttpResponse(str(doc.revision()))
def history(request, chunk_id):
# TODO: pagination
doc = get_object_or_404(Chunk, pk=chunk_id)
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
changes = []
for change in doc.history().order_by('-created_at'):
form = forms.DocumentPubmarkForm(request.POST, prefix="pubmark")
if form.is_valid():
doc = get_object_or_404(Chunk, pk=chunk_id)
+ if not doc.book.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
revision = form.cleaned_data['revision']
publishable = form.cleaned_data['publishable']
APICLIENT_WL_CONSUMER_KEY = None
APICLIENT_WL_CONSUMER_SECRET = None
+
+USE_CELERY = True
import tempfile
CATALOGUE_REPO_PATH = tempfile.mkdtemp(prefix='redakcja-repo')
+USE_CELERY = False
INSTALLED_APPS += ('django_nose', 'dvcs.tests')
.editable td {
padding: 1px;
}
-.editable input, .editable select {
- width: 30em;
+.editable input, .editable select, .editable textarea {
+ width: 400px;
}
#login-box {
self.galleryImages = data;
params['success'](self, data);
},
- error: function() {
+ error: function(xhr) {
+ switch (xhr.status) {
+ case 403:
+ var msg = 'Galerie dostępne tylko dla zalogowanych użytkowników.';
+ break;
+ case 404:
+ var msg = "Nie znaleziono galerii o nazwie: '" + self.galleryLink + "'.";
+ default:
+ var msg = "Nie udało się wczytać galerii o nazwie: '" + self.galleryLink + "'.";
+ }
self.galleryImages = [];
- params['failure'](self, "<p>Nie udało się wczytać galerii pod nazwą: '" + self.galleryLink + "'.</p>");
+ params['failure'](self, "<p>" + msg + "</p>");
}
});
};