From: Jan Szejko Date: Thu, 15 Dec 2016 11:43:14 +0000 (+0100) Subject: pep8, style, dead code cleanup etc. X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/31006b86a2e9883d8a4c5fe18128821b325773ab?ds=inline pep8, style, dead code cleanup etc. --- diff --git a/.gitignore b/.gitignore index a9dc4e6e..a040bd6f 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ nbproject/* .idea node_modules +static--wiki--editor--node_modules* /static_test chromedriver.log diff --git a/apps/build/management/commands/build.py b/apps/build/management/commands/build.py index 50097cff..f0d1df8f 100644 --- a/apps/build/management/commands/build.py +++ b/apps/build/management/commands/build.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# import os from subprocess import call from optparse import make_option @@ -9,25 +14,29 @@ from django.core.management import call_command class Command(BaseCommand): option_list = BaseCommand.option_list + ( - make_option('--node-bin-path', + make_option( + '--node-bin-path', action='store', dest='node_bin_path', type='string', default=None, help='Path to node binary'), - make_option('--npm-bin', + make_option( + '--npm-bin', action='store', dest='npm_bin', type='string', default='npm', help='Path to npm binary'), - make_option('--editor-npm-env', + make_option( + '--editor-npm-env', action='store', dest='editor_npm_env', type='string', default=None, help='Destination path of npm environment, defaults to ./node_modules'), - make_option('--editor-optimize', + make_option( + '--editor-optimize', action='store', dest='editor_optimize', type='string', @@ -48,7 +57,7 @@ class Command(BaseCommand): assert os.path.isdir(npm_env) os.symlink(npm_env, os.path.join(rng_base_dir, 'node_modules')) try: - call([options['npm_bin'], 'install'], cwd = rng_base_dir) + call([options['npm_bin'], 'install'], cwd=rng_base_dir) except OSError: raise CommandError('Something went wrong, propably npm binary not found. Tried: %s' % options['npm_bin']) @@ -56,9 +65,10 @@ class Command(BaseCommand): if options['node_bin_path']: # grunt needs npm binary to be foundable in PATH os.environ['PATH'] = '%s:%s' % (options['node_bin_path'], os.environ['PATH']) - args = ['./node_modules/.bin/grunt', 'build', '--output-dir=%s' % build_dir] + args = ['./node_modules/.bin/grunt', 'build', '--output-dir=%s' % build_dir] if options['editor_optimize']: args.append('--optimize=%s' % options['editor_optimize']) - call(args, cwd = rng_base_dir) + self.stdout.write('Calling %s at %s' % (' '.join(args), rng_base_dir)) + call(args, cwd=rng_base_dir) - call_command('collectstatic', interactive = False, ignore_patterns = ['editor']) + call_command('collectstatic', interactive=False, ignore_patterns=['editor']) diff --git a/apps/catalogue/admin.py b/apps/catalogue/admin.py index 93298174..454035bc 100644 --- a/apps/catalogue/admin.py +++ b/apps/catalogue/admin.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django.contrib import admin from catalogue import models diff --git a/apps/catalogue/ebook_utils.py b/apps/catalogue/ebook_utils.py index 56b73402..b27f0728 100644 --- a/apps/catalogue/ebook_utils.py +++ b/apps/catalogue/ebook_utils.py @@ -1,22 +1,7 @@ # -*- coding: utf-8 -*- -from StringIO import StringIO -#from catalogue.models import Book -#from librarian import DocProvider from django.http import HttpResponse -#~ class RedakcjaDocProvider(DocProvider): - #~ """Used for getting books' children.""" -#~ - #~ def __init__(self, publishable): - #~ self.publishable = publishable -#~ - #~ def by_slug(self, slug): - #~ return StringIO(Book.objects.get(dc_slug=slug - #~ ).materialize(publishable=self.publishable - #~ ).encode('utf-8')) - - def serve_file(file_path, name, mime_type): def read_chunks(f, size=8192): chunk = f.read(size) diff --git a/apps/catalogue/helpers.py b/apps/catalogue/helpers.py index 7e4ce526..e88c7e24 100644 --- a/apps/catalogue/helpers.py +++ b/apps/catalogue/helpers.py @@ -1,14 +1,17 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from datetime import date from functools import wraps from os.path import join -from os import listdir, stat +from os import listdir from shutil import move, rmtree from django.conf import settings import re import filecmp -from django.db.models import Count - def active_tab(tab): """ @@ -70,7 +73,7 @@ class GalleryMerger(object): @property def was_merged(self): - "Check if we have gallery size recorded" + """Check if we have gallery size recorded""" return self.dest_size is not None def merge(self): @@ -105,7 +108,8 @@ class GalleryMerger(object): for f in files: p = self.get_prefix(f) if p: - if p > last_pfx: last_pfx = p + if p > last_pfx: + last_pfx = p else: files_prefixed = False break @@ -121,7 +125,7 @@ class GalleryMerger(object): for f in files_other: pfx = self.get_prefix(f) if pfx is not None: - if not pfx in prefixes: + if pfx not in prefixes: last_pfx += 1 prefixes[pfx] = last_pfx renamed_files_other[f] = self.set_prefix(f, prefixes[pfx]) @@ -138,10 +142,10 @@ class GalleryMerger(object): # finally, move / rename files. for frm, to in renamed_files.items(): move(join(self.path(self.dest), frm), - join(self.path(self.dest), to)) + join(self.path(self.dest), to)) for frm, to in renamed_files_other.items(): move(join(self.path(self.src), frm), - join(self.path(self.dest), to)) + join(self.path(self.dest), to)) rmtree(join(self.path(self.src))) return self.dest @@ -149,7 +153,6 @@ class GalleryMerger(object): # Maybe subclass? def sstdocument(text): - #from catalogue.ebook_utils import RedakcjaDocProvider from librarian.document import Document return Document.from_string( diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo index 4149f307..c3fccf26 100644 Binary files a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo and b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.po b/apps/catalogue/locale/pl/LC_MESSAGES/django.po index d1a798c4..4a7d185a 100644 --- a/apps/catalogue/locale/pl/LC_MESSAGES/django.po +++ b/apps/catalogue/locale/pl/LC_MESSAGES/django.po @@ -261,7 +261,7 @@ msgstr "" #: templates/catalogue/book_text.html:80 #, python-format msgid "This resource has a published version." -msgstr "Ten zasób posiadaopublikowaną wersję." +msgstr "Ten zasób posiada opublikowaną wersję." #: templates/catalogue/book_text.html:83 msgid "This resource hasn't been published yet." diff --git a/apps/catalogue/management/__init__.py b/apps/catalogue/management/__init__.py deleted file mode 100644 index f7731d72..00000000 --- a/apps/catalogue/management/__init__.py +++ /dev/null @@ -1,122 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from collections import defaultdict -from django.db import transaction -from lxml import etree - - -class XmlUpdater(object): - """A base class for massive XML updates. - - In a subclass, override `fix_tree` and/or use `fixes_field` decorator. - Attributes: - * commit_desc: commits description - * retain_publishable: set publishable if head is (default: True) - * only_first_chunk: process only first chunks of books (default: False) - """ - commit_desc = "auto-update" - retain_publishable = True - only_first_chunk = False - - _element_fixers = defaultdict(list) - - def __init__(self): - self.counters = defaultdict(lambda: 0) - - @classmethod - def fixes_elements(cls, xpath): - """Decorator, registering a function as a fixer for given field type. - - Any decorated function will be called like - f(element, change=..., verbose=...) - providing changeset as context. - - :param xpath: element lookup, e.g. ".//{namespace-uri}tag-name" - :returns: True if anything changed - """ - def wrapper(fixer): - cls._element_fixers[xpath].append(fixer) - return fixer - return wrapper - - def fix_tree(self, tree, verbose): - """Override to provide general tree-fixing mechanism. - - :param tree: the parsed XML tree - :param verbose: verbosity level - :returns: True if anythig changed - """ - return False - - def fix_chunk(self, chunk, user, verbose=0, dry_run=False): - """Runs the update for a single chunk.""" - if verbose >= 2: - print chunk.get_absolute_url() - old_head = chunk.head - src = old_head.materialize() - try: - tree = etree.fromstring(src) - except: - if verbose: - print "%s: invalid XML" % chunk.get_absolute_url() - self.counters['Bad XML'] += 1 - return - - dirty = False - # Call the general fixing function. - if self.fix_tree(tree, verbose=verbose): - dirty = True - # Call the registered fixers. - for xpath, fixers in self._element_fixers.items(): - for elem in tree.findall(xpath): - for fixer in fixers: - if fixer(elem, change=old_head, verbose=verbose): - dirty = True - - if not dirty: - self.counters['Clean'] += 1 - return - - if not dry_run: - new_head = chunk.commit( - etree.tostring(tree, encoding=unicode), - author=user, - description=self.commit_desc - ) - if self.retain_publishable: - if old_head.publishable: - new_head.set_publishable(True) - if verbose >= 2: - print "done" - self.counters['Updated chunks'] += 1 - - def run(self, user, verbose=0, dry_run=False, books=None): - """Runs the actual update.""" - if books is None: - from catalogue.models import Book - books = Book.objects.all() - - # Start transaction management. - transaction.commit_unless_managed() - transaction.enter_transaction_management() - transaction.managed(True) - - for book in books: - self.counters['All books'] += 1 - chunks = book.chunk_set.all() - if self.only_first_chunk: - chunks = chunks[:1] - for chunk in chunks: - self.counters['All chunks'] += 1 - self.fix_chunk(chunk, user, verbose, dry_run) - - transaction.commit() - transaction.leave_transaction_management() - - def print_results(self): - """Prints the counters.""" - for item in sorted(self.counters.items()): - print "%s: %d" % item diff --git a/apps/catalogue/management/commands/__init__.py b/apps/catalogue/management/commands/__init__.py deleted file mode 100644 index e6f146f8..00000000 --- a/apps/catalogue/management/commands/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import sys -from optparse import make_option -from django.contrib.auth.models import User -from django.core.management.base import BaseCommand -from catalogue.models import Book - - -class XmlUpdaterCommand(BaseCommand): - """Base class for creating massive XML-updating commands. - - In a subclass, provide an XmlUpdater class in the `updater' attribute. - """ - option_list = BaseCommand.option_list + ( - make_option('-q', '--quiet', action='store_false', dest='verbose', - default=True, help='Less output'), - make_option('-d', '--dry-run', action='store_true', dest='dry_run', - default=False, help="Don't actually touch anything"), - make_option('-u', '--username', dest='username', metavar='USER', - help='Assign commits to this user (required, preferably yourself).'), - ) - args = "[slug]..." - - def handle(self, *args, **options): - verbose = options.get('verbose') - dry_run = options.get('dry_run') - username = options.get('username') - - if username: - user = User.objects.get(username=username) - else: - print 'Please provide a username.' - sys.exit(1) - - books = Book.objects.filter(slug__in=args) if args else None - - updater = self.updater() - updater.run(user, verbose=verbose, dry_run=dry_run, books=books) - updater.print_results() diff --git a/apps/catalogue/management/commands/fixdc.py b/apps/catalogue/management/commands/fixdc.py deleted file mode 100644 index 3f997d0c..00000000 --- a/apps/catalogue/management/commands/fixdc.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from librarian import RDFNS, WLURI, ValidationError -from librarian.dcparser import BookInfo -from catalogue.management import XmlUpdater -from catalogue.management.commands import XmlUpdaterCommand - - -class FixDC(XmlUpdater): - commit_desc = "auto-fixing DC" - retain_publishable = True - only_first_chunk = True - - def fix_wluri(elem, change, verbose): - try: - WLURI.strict(elem.text) - except ValidationError: - correct_field = unicode(WLURI.from_slug( - WLURI(elem.text.strip()).slug)) - try: - WLURI.strict(correct_field) - except ValidationError: - # Can't make a valid WLURI out of it, leave as is. - return False - if verbose: - print "Changing %s from %s to %s" % ( - elem.tag, elem.text, correct_field - ) - elem.text = correct_field - return True - for field in BookInfo.FIELDS: - if field.validator == WLURI: - XmlUpdater.fixes_elements('.//' + field.uri)(fix_wluri) - - @XmlUpdater.fixes_elements(".//" + RDFNS("Description")) - def fix_rdfabout(elem, change, verbose): - correct_about = change.tree.book.correct_about() - attr_name = RDFNS("about") - current_about = elem.get(attr_name) - if current_about != correct_about: - if verbose: - print "Changing rdf:about from %s to %s" % ( - current_about, correct_about - ) - elem.set(attr_name, correct_about) - return True - - -class Command(XmlUpdaterCommand): - updater = FixDC - help = 'Fixes obvious errors in DC: rdf:about and WLURI format.' diff --git a/apps/catalogue/management/commands/import_wl.py b/apps/catalogue/management/commands/import_wl.py deleted file mode 100644 index 5f603883..00000000 --- a/apps/catalogue/management/commands/import_wl.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf-8 -*- - -from collections import defaultdict -import json -from optparse import make_option -import urllib2 - -from django.core.management.base import BaseCommand -from django.core.management.color import color_style -from django.db import transaction -from librarian.dcparser import BookInfo -from librarian import ParseError, ValidationError - -from catalogue.models import Book - - -WL_API = 'http://www.wolnelektury.pl/api/books/' - - -class Command(BaseCommand): - option_list = BaseCommand.option_list + ( - make_option('-q', '--quiet', action='store_false', dest='verbose', default=True, - help='Less output'), - ) - help = 'Imports XML files from WL.' - - def handle(self, *args, **options): - - self.style = color_style() - - verbose = options.get('verbose') - - # Start transaction management. - transaction.commit_unless_managed() - transaction.enter_transaction_management() - transaction.managed(True) - - if verbose: - print 'Reading currently managed files (skipping hidden ones).' - slugs = defaultdict(list) - for b in Book.objects.exclude(slug__startswith='.').all(): - if verbose: - print b.slug - text = b.materialize().encode('utf-8') - try: - info = BookInfo.from_string(text) - except (ParseError, ValidationError): - pass - else: - slugs[info.slug].append(b) - - book_count = 0 - commit_args = { - "author_name": 'Platforma', - "description": 'Automatycznie zaimportowane z Wolnych Lektur', - "publishable": True, - } - - if verbose: - print 'Opening books list' - for book in json.load(urllib2.urlopen(WL_API)): - book_detail = json.load(urllib2.urlopen(book['href'])) - xml_text = urllib2.urlopen(book_detail['xml']).read() - info = BookInfo.from_string(xml_text) - previous_books = slugs.get(info.slug) - if previous_books: - if len(previous_books) > 1: - print self.style.ERROR("There is more than one book " - "with slug %s:"), - previous_book = previous_books[0] - comm = previous_book.slug - else: - previous_book = None - comm = '*' - print book_count, info.slug , '-->', comm - Book.import_xml_text(xml_text, title=info.title[:255], - slug=info.slug[:128], previous_book=previous_book, - commit_args=commit_args) - book_count += 1 - - # Print results - print - print "Results:" - print "Imported %d books from WL:" % ( - book_count, ) - print - - - transaction.commit() - transaction.leave_transaction_management() - diff --git a/apps/catalogue/models/__init__.py b/apps/catalogue/models/__init__.py index a940dd00..5e8adb3e 100755 --- a/apps/catalogue/models/__init__.py +++ b/apps/catalogue/models/__init__.py @@ -8,8 +8,6 @@ from catalogue.models.document import Document from catalogue.models.plan import Plan from catalogue.models.publish_log import PublishRecord from catalogue.models.tag import Category, Tag -# from catalogue.models.book import Book -# from catalogue.models.listeners import * from django.contrib.auth.models import User as AuthUser diff --git a/apps/catalogue/models/book.py b/apps/catalogue/models/book.py deleted file mode 100755 index 34ac796c..00000000 --- a/apps/catalogue/models/book.py +++ /dev/null @@ -1,436 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from django.contrib.auth.models import User -from django.contrib.sites.models import Site -from django.db import models, transaction -from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ -from django.conf import settings -from slughifi import slughifi - -import apiclient -from catalogue.helpers import cached_in_field, GalleryMerger -from catalogue.models import BookPublishRecord, ChunkPublishRecord, Project -from catalogue.signals import post_publish -from catalogue.tasks import refresh_instance, book_content_updated -from catalogue.xml_tools import compile_text, split_xml -from cover.models import Image -from organizations.models import Organization -import os -import shutil -import re - - -class Book(models.Model): - """ A document edited on the wiki """ - - 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(u'materiały', max_length=255, blank=True) - project = models.ForeignKey(Project, null=True, blank=True) - - owner_user = models.ForeignKey(User, null=True) - owner_organization = models.ForeignKey(Organization, null=True) - - #wl_slug = models.CharField(_('title'), max_length=255, null=True, db_index=True, editable=False) - parent = models.ForeignKey('self', null=True, blank=True, verbose_name=_('parent'), related_name="children", editable=False) - parent_number = models.IntegerField(_('parent number'), null=True, blank=True, db_index=True, editable=False) - - # Cache - _short_html = models.TextField(null=True, blank=True, editable=False) - _single = models.NullBooleanField(editable=False, db_index=True) - _new_publishable = models.NullBooleanField(editable=False) - _published = models.NullBooleanField(editable=False) - _on_track = models.IntegerField(null=True, blank=True, db_index=True, editable=False) - dc_cover_image = models.ForeignKey(Image, blank=True, null=True, - db_index=True, on_delete=models.SET_NULL, editable=False) - dc_slug = models.CharField(max_length=128, null=True, blank=True, - editable=False, db_index=True) - - class NoTextError(BaseException): - pass - - class Meta: - app_label = 'catalogue' - ordering = ['title', 'slug'] - verbose_name = u'moduł' - verbose_name_plural = u'moduły' - - - # Representing - # ============ - - def __iter__(self): - return iter(self.chunk_set.all()) - - def __getitem__(self, chunk): - return self.chunk_set.all()[chunk] - - def __len__(self): - return self.chunk_set.count() - - def __nonzero__(self): - """ - Necessary so that __len__ isn't used for bool evaluation. - """ - return True - - def __unicode__(self): - return self.title - - @models.permalink - def get_absolute_url(self): - return ("catalogue_book", [self.slug]) - - def correct_about(self): - return "http://%s%s" % ( - Site.objects.get_current().domain, - self.get_absolute_url() - ) - - # Creating & manipulating - # ======================= - - def accessible(self, request): - return self.public or request.user.is_authenticated() - - @classmethod - @transaction.commit_on_success - def create(cls, creator, text, *args, **kwargs): - b = cls.objects.create(*args, **kwargs) - b.chunk_set.all().update(creator=creator) - b[0].commit(text, author=creator) - return b - - def add(self, *args, **kwargs): - """Add a new chunk at the end.""" - return self.chunk_set.reverse()[0].split(*args, **kwargs) - - @classmethod - @transaction.commit_on_success - def import_xml_text(cls, text=u'', previous_book=None, - commit_args=None, **kwargs): - """Imports a book from XML, splitting it into chunks as necessary.""" - texts = split_xml(text) - if previous_book: - instance = previous_book - else: - instance = cls(**kwargs) - instance.save() - - # if there are more parts, set the rest to empty strings - book_len = len(instance) - for i in range(book_len - len(texts)): - texts.append((u'pusta część %d' % (i + 1), u'')) - - i = 0 - for i, (title, text) in enumerate(texts): - if not title: - title = u'część %d' % (i + 1) - - slug = slughifi(title) - - if i < book_len: - chunk = instance[i] - chunk.slug = slug[:50] - chunk.title = title[:255] - chunk.save() - else: - chunk = instance.add(slug, title) - - chunk.commit(text, **commit_args) - - return instance - - def make_chunk_slug(self, proposed): - """ - Finds a chunk slug not yet used in the book. - """ - slugs = set(c.slug for c in self) - i = 1 - new_slug = proposed[:50] - while new_slug in slugs: - new_slug = "%s_%d" % (proposed[:45], i) - i += 1 - return new_slug - - @transaction.commit_on_success - def append(self, other, slugs=None, titles=None): - """Add all chunks of another book to self.""" - assert self != other - - number = self[len(self) - 1].number + 1 - len_other = len(other) - single = len_other == 1 - - if slugs is not None: - assert len(slugs) == len_other - if titles is not None: - assert len(titles) == len_other - if slugs is None: - slugs = [slughifi(t) for t in titles] - - for i, chunk in enumerate(other): - # move chunk to new book - chunk.book = self - chunk.number = number - - if titles is None: - # try some title guessing - if other.title.startswith(self.title): - other_title_part = other.title[len(self.title):].lstrip(' /') - else: - other_title_part = other.title - - if single: - # special treatment for appending one-parters: - # just use the guessed title and original book slug - chunk.title = other_title_part - if other.slug.startswith(self.slug): - chunk.slug = other.slug[len(self.slug):].lstrip('-_') - else: - chunk.slug = other.slug - else: - chunk.title = ("%s, %s" % (other_title_part, chunk.title))[:255] - else: - chunk.slug = slugs[i] - chunk.title = titles[i] - - chunk.slug = self.make_chunk_slug(chunk.slug) - chunk.save() - number += 1 - assert not other.chunk_set.exists() - - gm = GalleryMerger(self.gallery, other.gallery) - self.gallery = gm.merge() - - # and move the gallery starts - if gm.was_merged: - for chunk in self[len(self) - len_other:]: - old_start = chunk.gallery_start or 1 - chunk.gallery_start = old_start + gm.dest_size - gm.num_deleted - chunk.save() - - other.delete() - - - @transaction.commit_on_success - def prepend_history(self, other): - """Prepend history from all the other book's chunks to own.""" - assert self != other - - for i in range(len(self), len(other)): - title = u"pusta część %d" % i - chunk = self.add(slughifi(title), title) - chunk.commit('') - - for i in range(len(other)): - self[i].prepend_history(other[0]) - - assert not other.chunk_set.exists() - other.delete() - - def split(self): - """Splits all the chunks into separate books.""" - self.title - for chunk in self: - book = Book.objects.create(title=chunk.title, slug=chunk.slug, - public=self.public, gallery=self.gallery) - book[0].delete() - chunk.book = book - chunk.number = 1 - chunk.save() - assert not self.chunk_set.exists() - self.delete() - - # State & cache - # ============= - - def last_published(self): - try: - return self.publish_log.all()[0].timestamp - except IndexError: - return None - - def assert_publishable(self): - assert self.chunk_set.exists(), _('No chunks in the book.') - try: - changes = self.get_current_changes(publishable=True) - except self.NoTextError: - raise AssertionError(_('Not all chunks have publishable revisions.')) - - from librarian import NoDublinCore, ParseError, ValidationError - - try: - bi = self.wldocument(changes=changes, strict=True).book_info - except ParseError, e: - raise AssertionError(_('Invalid XML') + ': ' + unicode(e)) - except NoDublinCore: - raise AssertionError(_('No Dublin Core found.')) - except ValidationError, e: - raise AssertionError(_('Invalid Dublin Core') + ': ' + unicode(e)) - - valid_about = self.correct_about() - assert bi.about == valid_about, _("rdf:about is not") + " " + valid_about - - def publishable_error(self): - try: - return self.assert_publishable() - except AssertionError, e: - return e - else: - return None - - def hidden(self): - return self.slug.startswith('.') - - def is_new_publishable(self): - """Checks if book is ready for publishing. - - Returns True if there is a publishable version newer than the one - already published. - - """ - new_publishable = False - if not self.chunk_set.exists(): - return False - for chunk in self: - change = chunk.publishable() - if not change: - return False - if not new_publishable and not change.publish_log.exists(): - new_publishable = True - return new_publishable - new_publishable = cached_in_field('_new_publishable')(is_new_publishable) - - def is_published(self): - return self.publish_log.exists() - published = cached_in_field('_published')(is_published) - - def get_on_track(self): - if self.published: - return -1 - stages = [ch.stage.ordering if ch.stage is not None else 0 - for ch in self] - if not len(stages): - return 0 - return min(stages) - on_track = cached_in_field('_on_track')(get_on_track) - - def is_single(self): - return len(self) == 1 - single = cached_in_field('_single')(is_single) - - #@cached_in_field('_short_html') - def short_html(self): - return render_to_string('catalogue/book_list/book.html', {'book': self}) - - def book_info(self, publishable=True): - try: - book_xml = self.materialize(publishable=publishable) - except self.NoTextError: - pass - else: - from librarian.dcparser import BookInfo - from librarian import NoDublinCore, ParseError, ValidationError - try: - return BookInfo.from_string(book_xml.encode('utf-8')) - except (self.NoTextError, ParseError, NoDublinCore, ValidationError): - return None - - def refresh_dc_cache(self): - update = { - 'dc_slug': None, - 'dc_cover_image': None, - } - - info = self.book_info() - if info is not None: - update['dc_slug'] = info.url.slug - if info.cover_source: - try: - image = Image.objects.get(pk=int(info.cover_source.rstrip('/').rsplit('/', 1)[-1])) - except: - pass - else: - if info.cover_source == image.get_full_url(): - update['dc_cover_image'] = image - Book.objects.filter(pk=self.pk).update(**update) - - def touch(self): - # this should only really be done when text or publishable status changes - book_content_updated.delay(self) - - update = { - "_new_publishable": self.is_new_publishable(), - "_published": self.is_published(), - "_single": self.is_single(), - "_on_track": self.get_on_track(), - "_short_html": None, - } - Book.objects.filter(pk=self.pk).update(**update) - refresh_instance(self) - - def refresh(self): - """This should be done offline.""" - self.short_html - self.single - self.new_publishable - self.published - - # Materializing & publishing - # ========================== - - def get_current_changes(self, publishable=True): - """ - Returns a list containing one Change for every Chunk in the Book. - Takes the most recent revision (publishable, if set). - Throws an error, if a proper revision is unavailable for a Chunk. - """ - if publishable: - changes = [chunk.publishable() for chunk in self] - else: - changes = [chunk.head for chunk in self if chunk.head is not None] - if None in changes: - raise self.NoTextError('Some chunks have no available text.') - return changes - - def materialize(self, publishable=False, changes=None): - """ - Get full text of the document compiled from chunks. - Takes the current versions of all texts - or versions most recently tagged for publishing, - or a specified iterable changes. - """ - if changes is None: - changes = self.get_current_changes(publishable) - return compile_text(change.materialize() for change in changes) - - def wldocument(self, publishable=True, changes=None, - parse_dublincore=True, strict=False): - from catalogue.ebook_utils import RedakcjaDocProvider - from librarian.parser import WLDocument - - return WLDocument.from_string( - self.materialize(publishable=publishable, changes=changes), - provider=RedakcjaDocProvider(publishable=publishable), - parse_dublincore=parse_dublincore, - strict=strict) - - def publish(self, user): - """ - Publishes a book on behalf of a (local) user. - """ - self.assert_publishable() - changes = self.get_current_changes(publishable=True) - book_xml = self.materialize(changes=changes) - apiclient.api_call(user, "books/", {"book_xml": book_xml}) - # record the publish - br = BookPublishRecord.objects.create(book=self, user=user) - for c in changes: - ChunkPublishRecord.objects.create(book_record=br, change=c) - post_publish.send(sender=br) diff --git a/apps/catalogue/models/document.py b/apps/catalogue/models/document.py index 70f50f25..d58a4d1f 100755 --- a/apps/catalogue/models/document.py +++ b/apps/catalogue/models/document.py @@ -7,6 +7,7 @@ from __future__ import unicode_literals from datetime import date from django.conf import settings +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.db import models from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ @@ -53,7 +54,7 @@ class Document(Ref): if header is None: header = etree.fromstring(data).find('.//{http://nowoczesnapolska.org.pl/sst#}header') metadata['title'] = getattr(header, 'text', ' ') or ' ' - #print 'meta', d['title'] + # print 'meta', d['title'] m = t.find('metadata') if m is None: @@ -86,7 +87,7 @@ class Document(Ref): def get_plan(self): try: plan = self.plan_set.get(stage=self.stage) - except: + except (ObjectDoesNotExist, MultipleObjectsReturned): return None return plan diff --git a/apps/catalogue/models/listeners.py b/apps/catalogue/models/listeners.py deleted file mode 100755 index 91d39ffb..00000000 --- a/apps/catalogue/models/listeners.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from django.contrib.auth.models import User -from django.db import models -from catalogue.models import Book, Chunk -from catalogue.signals import post_publish - - -def book_changed(sender, instance, created, **kwargs): - instance.touch() - for c in instance: - c.touch() -models.signals.post_save.connect(book_changed, sender=Book) - - -def chunk_changed(sender, instance, created, **kwargs): - instance.book.touch() - instance.touch() -models.signals.post_save.connect(chunk_changed, sender=Chunk) - - -def user_changed(sender, instance, *args, **kwargs): - books = set() - for c in instance.chunk_set.all(): - books.add(c.book) - c.touch() - for b in books: - b.touch() -models.signals.post_save.connect(user_changed, sender=User) - - -def publish_listener(sender, *args, **kwargs): - sender.book.touch() - for c in sender.book: - c.touch() -post_publish.connect(publish_listener) - - -def listener_create(sender, instance, created, **kwargs): - if created: - instance.chunk_set.create(number=1, slug='1') -models.signals.post_save.connect(listener_create, sender=Book) - diff --git a/apps/catalogue/models/plan.py b/apps/catalogue/models/plan.py index ff5cd168..3f05fdf4 100644 --- a/apps/catalogue/models/plan.py +++ b/apps/catalogue/models/plan.py @@ -7,6 +7,7 @@ from django.conf import settings from django.db import models from catalogue.models import Document + class Plan(models.Model): document = models.ForeignKey(Document) stage = models.CharField(max_length=128) diff --git a/apps/catalogue/models/publish_log.py b/apps/catalogue/models/publish_log.py index cbbcf9ae..2dfff9ec 100755 --- a/apps/catalogue/models/publish_log.py +++ b/apps/catalogue/models/publish_log.py @@ -12,7 +12,7 @@ from dvcs.models import Revision class PublishRecord(models.Model): """A record left after publishing a Document.""" - document = models.ForeignKey('Document', verbose_name=_('document'), related_name='publish_log') + document = models.ForeignKey('catalogue.Document', verbose_name=_('document'), related_name='publish_log') revision = models.ForeignKey(Revision, verbose_name=_('revision'), related_name='publish_log') timestamp = models.DateTimeField(_('time'), auto_now_add=True) user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user')) @@ -20,4 +20,4 @@ class PublishRecord(models.Model): class Meta: ordering = ['-timestamp'] verbose_name = _('book publish record') - verbose_name = _('book publish records') + verbose_name_plural = _('book publish records') diff --git a/apps/catalogue/models/template.py b/apps/catalogue/models/template.py index dded859e..fcb2f4bb 100644 --- a/apps/catalogue/models/template.py +++ b/apps/catalogue/models/template.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django.db import models diff --git a/apps/catalogue/signals.py b/apps/catalogue/signals.py index 62ca5145..c884df74 100644 --- a/apps/catalogue/signals.py +++ b/apps/catalogue/signals.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django.dispatch import Signal post_publish = Signal() diff --git a/apps/catalogue/tasks.py b/apps/catalogue/tasks.py index c386c22b..9186705a 100644 --- a/apps/catalogue/tasks.py +++ b/apps/catalogue/tasks.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from celery.task import task from django.utils import translation @@ -11,6 +16,7 @@ def _refresh_by_pk(cls, pk, language=None): finally: translation.activate(prev_language) + def refresh_instance(instance): _refresh_by_pk.delay(type(instance), instance.pk, translation.get_language()) diff --git a/apps/catalogue/templates/catalogue/activity.html b/apps/catalogue/templates/catalogue/activity.html deleted file mode 100755 index c88b5388..00000000 --- a/apps/catalogue/templates/catalogue/activity.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} -{% load wall %} - - -{% block content %} - -

< - {% trans "Activity" %}: {{ day }} - {% if next_day %} - > - {% endif %} -

- - {% day_wall day %} -{% endblock content %} diff --git a/apps/catalogue/templates/catalogue/book_append_to.html b/apps/catalogue/templates/catalogue/book_append_to.html deleted file mode 100755 index 76a59621..00000000 --- a/apps/catalogue/templates/catalogue/book_append_to.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} - -{% block leftcolumn %} -
- {% csrf_token %} - {{ form.as_p }} - -

-
-{% endblock leftcolumn %} - -{% block rightcolumn %} -{% endblock rightcolumn %} diff --git a/apps/catalogue/templates/catalogue/book_detail.html b/apps/catalogue/templates/catalogue/book_detail.html index 1bdbad92..20a7d6da 100755 --- a/apps/catalogue/templates/catalogue/book_detail.html +++ b/apps/catalogue/templates/catalogue/book_detail.html @@ -15,94 +15,4 @@ {% endif %} -{% comment %} - - -{% if editable %}
{% csrf_token %}{% endif %} - - {{ form.as_table }} - {% if editable %} - - {% endif %} -
-{% if editable %}
{% endif %} - -{% if editable %} - {% if book.gallery %} -

{% trans "Edit gallery" %}

- {% endif %} - -

{% trans "Append to other book" %}

-{% endif %} - - -
- -

{% trans "Edit" %}

- - - {% for chunk in book %} - {{ chunk.short_html|safe }} - {% endfor %} -
- -
- - -
- - -

{% trans "Publication" %}

- -
- -{% if book.dc_cover_image %} - {{ book.dc_cover_image }} -{% endif %} -
- -

{% trans "Last published" %}: - {% if book.last_published %} - {{ book.last_published }} - {% else %} - — - {% endif %} -

- -{% if publishable %} -

- {% trans "Full XML" %}
- {% trans "HTML version" %}
- {% trans "TXT version" %}
- {% trans "PDF version" %}
- {% trans "EPUB version" %}
-

- - {% if user.is_authenticated %} - -
{% csrf_token %} - - - -
- {% else %} - {% trans "Log in to publish." %} - {% endif %} -{% else %} -

{% trans "This book can't be published yet, because:" %}

- -{% endif %} - -
-
- - -{% endcomment %} - {% endblock %} diff --git a/apps/catalogue/templates/catalogue/book_edit.html b/apps/catalogue/templates/catalogue/book_edit.html deleted file mode 100755 index 3fffa963..00000000 --- a/apps/catalogue/templates/catalogue/book_edit.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} - -{% block leftcolumn %} -
- {% csrf_token %} - {{ form.as_p }} - -

-
-{% endblock leftcolumn %} - -{% block rightcolumn %} -{% endblock rightcolumn %} diff --git a/apps/catalogue/templates/catalogue/book_list/chunk.html b/apps/catalogue/templates/catalogue/book_list/chunk.html deleted file mode 100755 index 7bdf3aa9..00000000 --- a/apps/catalogue/templates/catalogue/book_list/chunk.html +++ /dev/null @@ -1,26 +0,0 @@ -{% load i18n %} - - - - - [c] - - {{ chunk.number }}. - {{ chunk.title }} - {% if chunk.stage %} - {{ chunk.stage }} - {% else %} - – - {% endif %} - {% if chunk.user %} - - {{ chunk.user.first_name }} {{ chunk.user.last_name }} - {% else %} - - {% endif %} - - - {% if chunk.new_publishable %}p{% endif %} - {% if chunk.changed %}+{% endif %} - - diff --git a/apps/catalogue/templates/catalogue/chunk_add.html b/apps/catalogue/templates/catalogue/chunk_add.html deleted file mode 100755 index b287479c..00000000 --- a/apps/catalogue/templates/catalogue/chunk_add.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} - -{% block content %} -

{% trans "Split chunk" %}

- -
- {% csrf_token %} - - - - {{ form.as_table }} - -
{% trans "Insert empty chunk after" %}:{{ chunk.pretty_name }}
-
-{% endblock content %} diff --git a/apps/catalogue/templates/catalogue/chunk_edit.html b/apps/catalogue/templates/catalogue/chunk_edit.html deleted file mode 100755 index 3c1f3bf5..00000000 --- a/apps/catalogue/templates/catalogue/chunk_edit.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} - -{% block content %} -

{% trans "Chunk settings" %}

- -
- {% csrf_token %} - - - {{ form.as_table}} - -
{% trans "Book" %}:{{ chunk.book }} ({{ chunk.number }}/{{ chunk.book|length }})
- -
- - -

{% trans "Split chunk" %}

- -{% endblock content %} diff --git a/apps/catalogue/templates/catalogue/document_upload.html b/apps/catalogue/templates/catalogue/document_upload.html deleted file mode 100644 index 18637749..00000000 --- a/apps/catalogue/templates/catalogue/document_upload.html +++ /dev/null @@ -1,69 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} - - -{% block leftcolumn %} - - -

{% trans "Bulk documents upload" %}

- -

-{% trans "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with .xml will be ignored." %} -

- -
-{% csrf_token %} -{{ form.as_p }} -

-
- -
- -{% if error_list %} - -

{% trans "There have been some errors. No files have been added to the repository." %} -

{% trans "Offending files" %}

- - - {% if ok_list %} -

{% trans "Correct files" %}

- - {% endif %} - -{% else %} - - {% if ok_list %} -

{% trans "Files have been successfully uploaded to the repository." %}

-

{% trans "Uploaded files" %}

- - {% endif %} -{% endif %} - -{% if skipped_list %} -

{% trans "Skipped files" %}

-

{% trans "Files skipped due to no .xml extension" %}

- -{% endif %} - - -{% endblock leftcolumn %} - - -{% block rightcolumn %} -{% endblock rightcolumn %} diff --git a/apps/catalogue/templates/catalogue/user_list.html b/apps/catalogue/templates/catalogue/user_list.html deleted file mode 100755 index c4b1dfc4..00000000 --- a/apps/catalogue/templates/catalogue/user_list.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "catalogue/base.html" %} - -{% load i18n %} - -{% block leftcolumn %} - -

{% trans "Users" %}

- - - -{% endblock leftcolumn %} diff --git a/apps/catalogue/templates/catalogue/user_page.html b/apps/catalogue/templates/catalogue/user_page.html index 89b4ecef..1bfa97eb 100755 --- a/apps/catalogue/templates/catalogue/user_page.html +++ b/apps/catalogue/templates/catalogue/user_page.html @@ -1,15 +1 @@ {% extends "catalogue/base.html" %} - -{% load i18n %} -{% load catalogue book_list wall %} - - -{% block leftcolumn %} -

{{ viewed_user|nice_name }}

- {% book_list viewed_user %} -{% endblock leftcolumn %} - -{% block rightcolumn %} -

{% trans "Recent activity for" %} {{ viewed_user|nice_name }}

- {% wall viewed_user 10 %} -{% endblock rightcolumn %} diff --git a/apps/catalogue/templates/catalogue/wall.html b/apps/catalogue/templates/catalogue/wall.html deleted file mode 100755 index 25550fe6..00000000 --- a/apps/catalogue/templates/catalogue/wall.html +++ /dev/null @@ -1,35 +0,0 @@ -{% load i18n %} -{% load gravatar %} -{% load email %} - - diff --git a/apps/catalogue/templatetags/catalogue.py b/apps/catalogue/templatetags/catalogue.py index eceed763..a98aa62a 100644 --- a/apps/catalogue/templatetags/catalogue.py +++ b/apps/catalogue/templatetags/catalogue.py @@ -1,8 +1,11 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from __future__ import absolute_import -from django.core.urlresolvers import reverse from django import template -from django.utils.translation import ugettext as _ register = template.Library() @@ -23,15 +26,9 @@ def main_tabs(context): active = getattr(context['request'], 'catalogue_active_tab', None) tabs = [] - user = context['user'] - #tabs.append(Tab('my', _('My page'), reverse("catalogue_user"))) - - #tabs.append(Tab('activity', _('Activity'), reverse("catalogue_activity"))) - #tabs.append(Tab('all', _('All'), reverse("catalogue_document_list"))) - #tabs.append(Tab('users', _('Users'), reverse("catalogue_users"))) - - #if user.has_perm('catalogue.add_book'): - # tabs.append(Tab('create', _('Add'), reverse("catalogue_create_missing"))) + # tabs.append(Tab('my', _('My page'), reverse("catalogue_user"))) + # + # tabs.append(Tab('all', _('All'), reverse("catalogue_document_list"))) return {"tabs": tabs, "active_tab": active} @@ -39,4 +36,3 @@ def main_tabs(context): @register.filter def nice_name(user): return user.get_full_name() or user.username - diff --git a/apps/catalogue/templatetags/catalogue_files.py b/apps/catalogue/templatetags/catalogue_files.py index 8b6ea85f..6ee86f5a 100644 --- a/apps/catalogue/templatetags/catalogue_files.py +++ b/apps/catalogue/templatetags/catalogue_files.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django import template from django.conf import settings register = template.Library() @@ -8,4 +13,3 @@ def as_media_for(uri, document): if uri.startswith('file://'): uri = "https://milpeer.eu%suploads/%d/%s" % (settings.MEDIA_URL, document.pk, uri[len('file://'):]) return uri - diff --git a/apps/catalogue/templatetags/common_tags.py b/apps/catalogue/templatetags/common_tags.py index ccaf03bf..bbdf2d85 100755 --- a/apps/catalogue/templatetags/common_tags.py +++ b/apps/catalogue/templatetags/common_tags.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django import template register = template.Library() diff --git a/apps/catalogue/templatetags/document_list.py b/apps/catalogue/templatetags/document_list.py index b43de9e9..f9de2d6e 100755 --- a/apps/catalogue/templatetags/document_list.py +++ b/apps/catalogue/templatetags/document_list.py @@ -1,86 +1,18 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from __future__ import absolute_import -from re import split -from django.db.models import Q, Count +from django.db.models import Q from django import template from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.models import User from catalogue.models import Document register = template.Library() -class ChunksList(object): - def __init__(self, chunk_qs): - #self.chunk_qs = chunk_qs#.annotate( - #book_length=Count('book__chunk')).select_related( - #'book')#, 'stage__name', - #'user') - self.chunk_qs = chunk_qs.select_related('book__hidden') - - self.book_qs = chunk_qs.values('book_id') - - def __getitem__(self, key): - if isinstance(key, slice): - return self.get_slice(key) - elif isinstance(key, int): - return self.get_slice(slice(key, key+1))[0] - else: - raise TypeError('Unsupported list index. Must be a slice or an int.') - - def __len__(self): - return self.book_qs.count() - - def get_slice(self, slice_): - book_ids = [x['book_id'] for x in self.book_qs[slice_]] - chunk_qs = self.chunk_qs.filter(book__in=book_ids) - - chunks_list = [] - book = None - for chunk in chunk_qs: - if chunk.book != book: - book = chunk.book - chunks_list.append(ChoiceChunks(book, [chunk])) - else: - chunks_list[-1].chunks.append(chunk) - return chunks_list - - -class ChoiceChunks(object): - """ - Associates the given chunks iterable for a book. - """ - - chunks = None - - def __init__(self, book, chunks): - self.book = book - self.chunks = chunks - - -def foreign_filter(qs, value, filter_field, model, model_field='slug', unset='-'): - #print qs, value, filter_field, model, model_field, unset - if value == unset: - return qs.filter(**{filter_field: None}) - if not value: - return qs - try: - obj = model._default_manager.get(**{model_field: value}) - except model.DoesNotExist: - return qs.none() - else: - return qs.filter(**{filter_field: obj}) - - -def search_filter(qs, value, filter_fields): - if not value: - return qs - q = Q(**{"%s__icontains" % filter_fields[0]: value}) - for field in filter_fields[1:]: - q |= Q(**{"%s__icontains" % field: value}) - return qs.filter(q) - - _states = [ ('publishable', _('publishable'), Q(book___new_publishable=True)), ('changed', _('changed'), Q(_changed=True)), @@ -92,65 +24,38 @@ _states_options = [s[:2] for s in _states] _states_dict = dict([(s[0], s[2]) for s in _states]) -def document_list_filter(request, **kwargs): - - def arg_or_GET(field): - return kwargs.get(field, request.GET.get(field)) - - if arg_or_GET('all'): - chunks = Chunk.objects.all() - else: - chunks = Chunk.visible_objects.all() - - 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]) - - chunks = foreign_filter(chunks, arg_or_GET('user'), 'user', User, 'username') - chunks = foreign_filter(chunks, arg_or_GET('stage'), 'stage', Chunk.tag_model, 'slug') - chunks = search_filter(chunks, arg_or_GET('title'), ['book__title', 'title']) - chunks = foreign_filter(chunks, arg_or_GET('project'), 'book__project', Project, 'pk') - return chunks - - @register.inclusion_tag('catalogue/book_list/book_list.html', takes_context=True) def document_list(context, user=None, organization=None): request = context['request'] - #~ if user: - #~ filters = {"user": user} - #~ new_context = {"viewed_user": user} - #~ else: - #~ filters = {} - #~ new_context = { - #~ "users": User.objects.annotate( - #~ count=Count('chunk')).filter(count__gt=0).order_by( - #~ '-count', 'last_name', 'first_name'), - #~ "other_users": User.objects.annotate( - #~ count=Count('chunk')).filter(count=0).order_by( - #~ 'last_name', 'first_name'), - #~ } + # if user: + # filters = {"user": user} + # new_context = {"viewed_user": user} + # else: + # filters = {} + # new_context = { + # "users": User.objects.annotate( + # count=Count('chunk')).filter(count__gt=0).order_by( + # '-count', 'last_name', 'first_name'), + # "other_users": User.objects.annotate( + # count=Count('chunk')).filter(count=0).order_by( + # 'last_name', 'first_name'), + # } docs = Document.objects.filter(deleted=False) if user is not None: - docs = docs.filter(Q(owner_user=user) | Q(owner_organization__membership__user=user) | Q(assigned_to=user)).distinct() + docs = docs.filter( + Q(owner_user=user) | Q(owner_organization__membership__user=user) | Q(assigned_to=user)).distinct() if organization is not None: docs = docs.filter(owner_organization=organization) new_context = {} new_context.update({ - #"filters": True, + # "filters": True, "request": request, "books": docs, - #"books": ChunksList(document_list_filter(request, **filters)), - #"stages": Chunk.tag_model.objects.all(), - #"states": _states_options, - #"projects": Project.objects.all(), + # "stages": Chunk.tag_model.objects.all(), + # "states": _states_options, }) return new_context diff --git a/apps/catalogue/templatetags/flat_lang.py b/apps/catalogue/templatetags/flat_lang.py index 35577787..91590706 100755 --- a/apps/catalogue/templatetags/flat_lang.py +++ b/apps/catalogue/templatetags/flat_lang.py @@ -1,3 +1,9 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.utils import translation from django import template @@ -9,6 +15,5 @@ register = template.Library() def flat_lang(page): try: return type(page).objects.get(url="%s%s/" % (page.url, translation.get_language())) - except: + except (ObjectDoesNotExist, MultipleObjectsReturned): return page - diff --git a/apps/catalogue/templatetags/set_get_parameter.py b/apps/catalogue/templatetags/set_get_parameter.py index b3d44d73..87b3e7f8 100755 --- a/apps/catalogue/templatetags/set_get_parameter.py +++ b/apps/catalogue/templatetags/set_get_parameter.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from re import split from django import template @@ -31,7 +36,7 @@ class SetGetParameter(template.Node): del(params[key]) else: params[key] = template.Variable(value).resolve(context) - return '?%s' % params.urlencode() + return '?%s' % params.urlencode() @register.tag diff --git a/apps/catalogue/templatetags/wall.py b/apps/catalogue/templatetags/wall.py deleted file mode 100755 index 28671fb7..00000000 --- a/apps/catalogue/templatetags/wall.py +++ /dev/null @@ -1,155 +0,0 @@ -from __future__ import absolute_import - -from datetime import timedelta -from django.db.models import Q -from django.core.urlresolvers import reverse -from django.contrib.comments.models import Comment -from django import template -from django.utils.translation import ugettext as _ - -from catalogue.models import Chunk, BookPublishRecord - -register = template.Library() - - -class WallItem(object): - title = '' - summary = '' - url = '' - timestamp = '' - user = None - user_name = '' - email = '' - - def __init__(self, tag): - self.tag = tag - - def get_email(self): - if self.user: - return self.user.email - else: - return self.email - - -def changes_wall(user=None, max_len=None, day=None): - qs = Chunk.change_model.objects.order_by('-created_at') - qs = qs.select_related('author', 'tree', 'tree__book__title') - if user is not None: - qs = qs.filter(Q(author=user) | Q(tree__user=user)) - if max_len is not None: - qs = qs[:max_len] - if day is not None: - next_day = day + timedelta(1) - qs = qs.filter(created_at__gte=day, created_at__lt=next_day) - for item in qs: - tag = 'stage' if item.tags.count() else 'change' - chunk = item.tree - w = WallItem(tag) - if user and item.author != user: - w.header = _('Related edit') - else: - w.header = _('Edit') - w.title = chunk.pretty_name() - w.summary = item.description - w.url = reverse('wiki_editor', - args=[chunk.book.slug, chunk.slug]) + '?diff=%d' % item.revision - w.timestamp = item.created_at - w.user = item.author - w.user_name = item.author_name - w.email = item.author_email - yield w - - -# TODO: marked for publishing - - -def published_wall(user=None, max_len=None, day=None): - qs = BookPublishRecord.objects.select_related('book__title') - if user: - # TODO: published my book - qs = qs.filter(Q(user=user)) - if max_len is not None: - qs = qs[:max_len] - if day is not None: - next_day = day + timedelta(1) - qs = qs.filter(timestamp__gte=day, timestamp__lt=next_day) - for item in qs: - w = WallItem('publish') - w.header = _('Publication') - w.title = item.book.title - w.timestamp = item.timestamp - w.url = item.book.get_absolute_url() - w.user = item.user - w.email = item.user.email - yield w - - -def comments_wall(user=None, max_len=None, day=None): - qs = Comment.objects.filter(is_public=True).select_related().order_by('-submit_date') - if user: - # TODO: comments concerning my books - qs = qs.filter(Q(user=user)) - if max_len is not None: - qs = qs[:max_len] - if day is not None: - next_day = day + timedelta(1) - qs = qs.filter(submit_date__gte=day, submit_date__lt=next_day) - for item in qs: - w = WallItem('comment') - w.header = _('Comment') - w.title = item.content_object - w.summary = item.comment - w.url = item.content_object.get_absolute_url() - w.timestamp = item.submit_date - w.user = item.user - ui = item.userinfo - w.email = item.email - w.user_name = item.name - yield w - - -def big_wall(walls, max_len=None): - """ - Takes some WallItem iterators and zips them into one big wall. - Input iterators must already be sorted by timestamp. - """ - subwalls = [] - for w in walls: - try: - subwalls.append([next(w), w]) - except StopIteration: - pass - - if max_len is None: - max_len = -1 - while max_len and subwalls: - i, next_item = max(enumerate(subwalls), key=lambda x: x[1][0].timestamp) - yield next_item[0] - max_len -= 1 - try: - next_item[0] = next(next_item[1]) - except StopIteration: - del subwalls[i] - - -@register.inclusion_tag("catalogue/wall.html", takes_context=True) -def wall(context, user=None, max_len=100): - return { - "request": context['request'], - "STATIC_URL": context['STATIC_URL'], - "wall": big_wall([ - changes_wall(user, max_len), - published_wall(user, max_len), - comments_wall(user, max_len), - ], max_len)} - -@register.inclusion_tag("catalogue/wall.html", takes_context=True) -def day_wall(context, day): - return { - "request": context['request'], - "STATIC_URL": context['STATIC_URL'], - "wall": big_wall([ - changes_wall(day=day), - published_wall(day=day), - comments_wall(day=day), - ])} diff --git a/apps/catalogue/tests/__init__.py b/apps/catalogue/tests/__init__.py index 533a6c53..b685324b 100644 --- a/apps/catalogue/tests/__init__.py +++ b/apps/catalogue/tests/__init__.py @@ -3,7 +3,3 @@ # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # -from catalogue.tests.book import * -from catalogue.tests.gallery import * -from catalogue.tests.publish import * -from catalogue.tests.xml_updater import * diff --git a/apps/catalogue/tests/book.py b/apps/catalogue/tests/book.py deleted file mode 100644 index df6f3b4f..00000000 --- a/apps/catalogue/tests/book.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -"""Tests for manipulating books in the catalogue.""" - -from django.test import TestCase -from django.contrib.auth.models import User -from catalogue.models import Book - - -class ManipulationTests(TestCase): - - def setUp(self): - self.user = User.objects.create(username='tester') - self.book1 = Book.create(self.user, 'book 1', slug='book1') - self.book2 = Book.create(self.user, 'book 2', slug='book2') - - def test_append(self): - self.book1.append(self.book2) - self.assertEqual(Book.objects.all().count(), 1) - self.assertEqual(len(self.book1), 2) - - def test_append_to_self(self): - with self.assertRaises(AssertionError): - self.book1.append(Book.objects.get(pk=self.book1.pk)) - self.assertEqual(Book.objects.all().count(), 2) - self.assertEqual(len(self.book1), 1) - - def test_prepend_history(self): - self.book1.prepend_history(self.book2) - self.assertEqual(Book.objects.all().count(), 1) - self.assertEqual(len(self.book1), 1) - self.assertEqual(self.book1.materialize(), 'book 1') - - def test_prepend_history_to_self(self): - with self.assertRaises(AssertionError): - self.book1.prepend_history(self.book1) - self.assertEqual(Book.objects.all().count(), 2) - self.assertEqual(self.book1.materialize(), 'book 1') - self.assertEqual(self.book2.materialize(), 'book 2') - - def test_split_book(self): - self.book1.chunk_set.create(number=2, title='Second chunk', - slug='book3') - self.book1[1].commit('I survived!') - self.assertEqual(len(self.book1), 2) - self.book1.split() - self.assertEqual(set([b.slug for b in Book.objects.all()]), - set(['book2', '1', 'book3'])) - self.assertEqual( - Book.objects.get(slug='book3').materialize(), - 'I survived!') diff --git a/apps/catalogue/tests/files/chunk1.xml b/apps/catalogue/tests/files/chunk1.xml deleted file mode 100755 index 6a75580a..00000000 --- a/apps/catalogue/tests/files/chunk1.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - -Mickiewicz, Adam -Do M*** -Fundacja Nowoczesna Polska -Romantyzm -Liryka -Wiersz - -http://wolnelektury.pl/katalog/lektura/sonety-odeskie-do-m -http://www.polona.pl/Content/2222 -Mickiewicz, Adam (1798-1855), Poezje, tom 1 (Wiersze młodzieńcze - Ballady i romanse - Wiersze do r. 1824), Krakowska Spółdzielnia Wydawnicza, wyd. 2 zwiększone, Kraków, 1922 - -Domena publiczna - Adam Mickiewicz zm. 1855 -1926 -xml -text -text -2007-09-06 -pol - - - - -Adam Mickiewicz -Sonety odeskie -Do M*** - -Wiérsz napisany w roku 1822 - - -Precz z moich oczu!... posłucham od razu,/ -Precz z mego serca!... i serce posłucha,/ -Precz z méj pamięci!... Nie! tego rozkazu/ -Moja i twoja pamięć nie posłucha. - - - - diff --git a/apps/catalogue/tests/files/chunk2.xml b/apps/catalogue/tests/files/chunk2.xml deleted file mode 100755 index 63a243e1..00000000 --- a/apps/catalogue/tests/files/chunk2.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - -Jak cień tém dłuższy, gdy padnie z daleka,/ -Tém szerzéj koło żałobne roztoczy,/ -Tak moja postać, im daléj ucieka,/ -Tém grubszym kirem twą pamięć pomroczy. - - - - diff --git a/apps/catalogue/tests/files/expected.xml b/apps/catalogue/tests/files/expected.xml deleted file mode 100755 index ff225a03..00000000 --- a/apps/catalogue/tests/files/expected.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - -Mickiewicz, Adam -Do M*** -Fundacja Nowoczesna Polska -Romantyzm -Liryka -Wiersz - -http://wolnelektury.pl/katalog/lektura/sonety-odeskie-do-m -http://www.polona.pl/Content/2222 -Mickiewicz, Adam (1798-1855), Poezje, tom 1 (Wiersze młodzieńcze - Ballady i romanse - Wiersze do r. 1824), Krakowska Spółdzielnia Wydawnicza, wyd. 2 zwiększone, Kraków, 1922 - -Domena publiczna - Adam Mickiewicz zm. 1855 -1926 -xml -text -text -2007-09-06 -pol - - - - -Adam Mickiewicz -Sonety odeskie -Do M*** - -Wiérsz napisany w roku 1822 - - -Precz z moich oczu!... posłucham od razu,/ -Precz z mego serca!... i serce posłucha,/ -Precz z méj pamięci!... Nie! tego rozkazu/ -Moja i twoja pamięć nie posłucha. - - - -Jak cień tém dłuższy, gdy padnie z daleka,/ -Tém szerzéj koło żałobne roztoczy,/ -Tak moja postać, im daléj ucieka,/ -Tém grubszym kirem twą pamięć pomroczy. - - - - diff --git a/apps/catalogue/tests/gallery.py b/apps/catalogue/tests/gallery.py deleted file mode 100644 index ad0dfd7d..00000000 --- a/apps/catalogue/tests/gallery.py +++ /dev/null @@ -1,148 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -"""Tests for galleries of scans.""" - -from os.path import join, basename, exists -from os import makedirs, listdir -from django.test import TestCase -from django.contrib.auth.models import User -from catalogue.models import Book -from tempfile import mkdtemp -from django.conf import settings - - -class GalleryAppendTests(TestCase): - def setUp(self): - self.user = User.objects.create(username='tester') - self.book1 = Book.create(self.user, 'book 1', slug='book1') - self.book1.chunk_set.create(number=2, title='Second chunk', - slug='book 1 / 2') - c=self.book1[1] - c.gallery_start=3 - - self.scandir = join(settings.MEDIA_ROOT, settings.IMAGE_DIR) - if not exists(self.scandir): - makedirs(self.scandir) - - def make_gallery(self, book, files): - d = mkdtemp('gallery', dir=self.scandir) - for named, cont in files.items(): - f = open(join(d, named), 'w') - f.write(cont) - f.close() - book.gallery = basename(d) - - - def test_both_indexed(self): - self.book2 = Book.create(self.user, 'book 2', slug='book2') - self.book2.chunk_set.create(number=2, title='Second chunk of second book', - slug='book 2 / 2') - - c = self.book2[1] - c.gallery_start = 3 - c.save() - - print "gallery starts:",self.book2[0].gallery_start, self.book2[1].gallery_start - - self.make_gallery(self.book1, { - '1-0001_1l' : 'aa', - '1-0001_2r' : 'bb', - '1-0002_1l' : 'cc', - '1-0002_2r' : 'dd', - }) - - self.make_gallery(self.book2, { - '1-0001_1l' : 'dd', # the same, should not be moved - '1-0001_2r' : 'ff', - '2-0002_1l' : 'gg', - '2-0002_2r' : 'hh', - }) - - self.book1.append(self.book2) - - files = listdir(join(self.scandir, self.book1.gallery)) - files.sort() - print files - self.assertEqual(files, [ - '1-0001_1l', - '1-0001_2r', - '1-0002_1l', - '1-0002_2r', - # '2-0001_1l', - '2-0001_2r', - '3-0002_1l', - '3-0002_2r', - ]) - - self.assertEqual((4, 6), (self.book1[2].gallery_start, self.book1[3].gallery_start)) - - - def test_none_indexed(self): - self.book2 = Book.create(self.user, 'book 2', slug='book2') - self.make_gallery(self.book1, { - '0001_1l' : 'aa', - '0001_2r' : 'bb', - '0002_1l' : 'cc', - '0002_2r' : 'dd', - }) - - self.make_gallery(self.book2, { - '0001_1l' : 'ee', - '0001_2r' : 'ff', - '0002_1l' : 'gg', - '0002_2r' : 'hh', - }) - - self.book1.append(self.book2) - - files = listdir(join(self.scandir, self.book1.gallery)) - files.sort() - print files - self.assertEqual(files, [ - '0-0001_1l', - '0-0001_2r', - '0-0002_1l', - '0-0002_2r', - '1-0001_1l', - '1-0001_2r', - '1-0002_1l', - '1-0002_2r', - ]) - - - def test_none_indexed(self): - import nose.tools - self.book2 = Book.create(self.user, 'book 2', slug='book2') - self.make_gallery(self.book1, { - '1-0001_1l' : 'aa', - '1-0001_2r' : 'bb', - '1002_1l' : 'cc', - '1002_2r' : 'dd', - }) - - self.make_gallery(self.book2, { - '0001_1l' : 'ee', - '0001_2r' : 'ff', - '0002_1l' : 'gg', - '0002_2r' : 'hh', - }) - - self.book1.append(self.book2) - - files = listdir(join(self.scandir, self.book1.gallery)) - files.sort() - print files - self.assertEqual(files, [ - '0-1-0001_1l', - '0-1-0001_2r', - '0-1002_1l', - '0-1002_2r', - '1-0001_1l', - '1-0001_2r', - '1-0002_1l', - '1-0002_2r', - ]) - diff --git a/apps/catalogue/tests/publish.py b/apps/catalogue/tests/publish.py deleted file mode 100644 index 9f0b8ca4..00000000 --- a/apps/catalogue/tests/publish.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -"""Tests for the publishing process.""" - -from catalogue.test_utils import get_fixture - -from mock import patch -from django.test import TestCase -from django.contrib.auth.models import User -from catalogue.models import Book - - -class PublishTests(TestCase): - def setUp(self): - self.user = User.objects.create(username='tester') - self.text1 = get_fixture('chunk1.xml') - self.book = Book.create(self.user, self.text1, slug='test-book') - - @patch('apiclient.api_call') - def test_unpublishable(self, api_call): - with self.assertRaises(AssertionError): - self.book.publish(self.user) - - @patch('apiclient.api_call') - def test_publish(self, api_call): - self.book[0].head.set_publishable(True) - self.book.publish(self.user) - api_call.assert_called_with(self.user, 'books/', {"book_xml": self.text1}) - - @patch('apiclient.api_call') - def test_publish_multiple(self, api_call): - self.book[0].head.set_publishable(True) - self.book[0].split(slug='part-2') - self.book[1].commit(get_fixture('chunk2.xml')) - self.book[1].head.set_publishable(True) - self.book.publish(self.user) - api_call.assert_called_with(self.user, 'books/', {"book_xml": get_fixture('expected.xml')}) diff --git a/apps/catalogue/tests/xml_updater.py b/apps/catalogue/tests/xml_updater.py deleted file mode 100644 index 9fb5a4a0..00000000 --- a/apps/catalogue/tests/xml_updater.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -"""XmlUpdater tests.""" - -from catalogue.test_utils import get_fixture -from django.test import TestCase -from django.contrib.auth.models import User -from catalogue.models import Book -from catalogue.management import XmlUpdater -from librarian import DCNS - - -class XmlUpdaterTests(TestCase): - class SimpleUpdater(XmlUpdater): - @XmlUpdater.fixes_elements('.//' + DCNS('title')) - def fix_title(element, **kwargs): - element.text = element.text + " fixed" - return True - - def setUp(self): - self.user = User.objects.create(username='tester') - text = get_fixture('chunk1.xml') - Book.create(self.user, text, slug='test-book') - self.title = "Do M***" - - def test_xml_updater(self): - self.SimpleUpdater().run(self.user) - self.assertEqual( - Book.objects.get(slug='test-book').wldocument( - publishable=False).book_info.title, - self.title + " fixed" - ) diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index 8a9945ab..148851b2 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 from django.conf.urls import patterns, url -from django.contrib.auth.decorators import permission_required, login_required -from catalogue.views import GalleryView, GalleryPackageView +from django.contrib.auth.decorators import login_required +from catalogue.views import GalleryView urlpatterns = patterns( @@ -11,15 +11,8 @@ urlpatterns = patterns( url(r'^upcoming/$', 'upcoming', name='catalogue_upcoming'), url(r'^finished/$', 'finished', name='catalogue_finished'), - # url(r'^catalogue/$', 'document_list', name='catalogue_document_list'), url(r'^user/$', 'my', name='catalogue_user'), url(r'^user/(?P[^/]+)/$', 'user', name='catalogue_user'), - url(r'^users/$', 'users', name='catalogue_users'), - url(r'^activity/$', 'activity', name='catalogue_activity'), - url(r'^activity/(?P\d{4}-\d{2}-\d{2})/$', - 'activity', name='catalogue_activity'), - - # url(r'^upload/$', 'upload', name='catalogue_upload'), url(r'^create/(?P[^/]*)/', 'create_missing', name='catalogue_create_missing'), @@ -30,43 +23,17 @@ urlpatterns = patterns( url(r'^doc/(?P\d+)/publish$', 'publish', name="catalogue_publish"), url(r'^doc/(?P\d+)/unpublish$', 'unpublish', name="catalogue_unpublish"), - # url(r'^(?P[^/]+)/publish/(?P\d+)$', 'publish', name="catalogue_publish"), url(r'^(?P[^/]+)/schedule/$', 'book_schedule', name="catalogue_book_schedule"), url(r'^(?P[^/]+)/owner/$', 'book_owner', name="catalogue_book_owner"), url(r'^(?P[^/]+)/delete/$', 'book_delete', name="catalogue_book_delete"), - # url(r'^book/(?P[^/]+)/$', 'book', name="catalogue_book"), - url(r'^(?P[^/]+)/attachments/$', login_required()(GalleryView.as_view()), name="catalogue_book_gallery"), - # url(r'^attachments/$', - # login_required()(GalleryView.as_view()), - # name="catalogue_attachments"), - # url(r'^attachments/(?P\d+)/$', - # login_required()(GalleryView.as_view()), - # name="catalogue_attachments"), - url(r'^book/(?P[^/]+)/gallery/package$', - permission_required('catalogue.change_book')(GalleryPackageView.as_view()), - name="catalogue_book_gallery_package"), - # url(r'^book/(?P[^/]+)/xml$', 'book_xml', name="catalogue_book_xml"), - # url(r'^book/(?P[^/]+)/txt$', 'book_txt', name="catalogue_book_txt"), url(r'^(?P\d+)/$', 'book_html', name="catalogue_html"), url(r'^(?P\d+)/preview/$', 'book_html', {'preview': True}, name="catalogue_preview"), url(r'^(?P\d+)/rev(?P\d+)/preview/$', 'book_html', {'preview': True}, name="catalogue_preview_rev"), url(r'^(?P\d+)/rev(?P\d+)/pdf/$', 'book_pdf', name="catalogue_pdf"), url(r'^(?P\d+)/rev(?P\d+)/epub/$', 'book_epub', name="catalogue_epub"), - - # url(r'^book/(?P[^/]+)/epub$', 'book_epub', name="catalogue_book_epub"), - # url(r'^book/(?P[^/]+)/pdf$', 'book_pdf', name="catalogue_book_pdf"), - - # url(r'^chunk_add/(?P[^/]+)/(?P[^/]+)/$', - # 'chunk_add', name="catalogue_chunk_add"), - # url(r'^chunk_edit/(?P[^/]+)/(?P[^/]+)/$', - # 'chunk_edit', name="catalogue_chunk_edit"), - # url(r'^book_append/(?P[^/]+)/$', - # 'book_append', name="catalogue_book_append"), - # url(r'^chunk_mass_edit', - # 'chunk_mass_edit', name='catalogue_chunk_mass_edit'), ) diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index d116fcb1..1d1b36ec 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- -from datetime import date, timedelta +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# import logging import os import shutil @@ -9,7 +12,6 @@ from django.contrib import auth from django.contrib.auth.models import User from django.contrib.auth.decorators import login_required from django.core.urlresolvers import reverse -from django.db.models import Count from django import http from django.http import Http404 from django.shortcuts import get_object_or_404, render, redirect @@ -18,14 +20,13 @@ from django.utils.http import urlquote_plus from django.views.decorators.http import require_POST from catalogue import forms -from catalogue import helpers from catalogue.helpers import active_tab from librarian import BuildError from .constants import STAGES from .models import Document, Plan from dvcs.models import Revision from organizations.models import Organization -from fileupload.views import UploadView, PackageView +from fileupload.views import UploadView # # Quick hack around caching problems, TODO: use ETags @@ -60,38 +61,12 @@ def my(request): }) -@active_tab('users') -def users(request): - return render(request, 'catalogue/user_list.html', { - 'users': User.objects.all().annotate(count=Count('chunk')).order_by( - '-count', 'last_name', 'first_name'), - }) - - -@active_tab('activity') -def activity(request, isodate=None): - today = date.today() - try: - day = helpers.parse_isodate(isodate) - except ValueError: - day = today - - if day > today: - raise Http404 - if day != today: - next_day = day + timedelta(1) - prev_day = day - timedelta(1) - - return render(request, 'catalogue/activity.html', locals()) - - @never_cache def logout_then_redirect(request): auth.logout(request) return http.HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?=')) -# @permission_required('catalogue.add_book') @login_required @active_tab('create') def create_missing(request): @@ -171,96 +146,6 @@ def create_missing(request): }) -# @permission_required('catalogue.add_book') -# @active_tab('upload') -# def upload(request): -# if request.method == "POST": -# form = forms.DocumentsUploadForm(request.POST, request.FILES) -# if form.is_valid(): -# import slughifi -# -# if request.user.is_authenticated(): -# creator = request.user -# else: -# creator = None -# -# zip = form.cleaned_data['zip'] -# skipped_list = [] -# ok_list = [] -# error_list = [] -# slugs = {} -# existing = [book.slug for book in Book.objects.all()] -# for filename in zip.namelist(): -# if filename[-1] == '/': -# continue -# title = os.path.basename(filename)[:-4] -# slug = slughifi(title) -# if not (slug and filename.endswith('.xml')): -# skipped_list.append(filename) -# elif slug in slugs: -# error_list.append((filename, slug, _('Slug already used for %s' % slugs[slug]))) -# elif slug in existing: -# error_list.append((filename, slug, _('Slug already used in repository.'))) -# else: -# try: -# zip.read(filename).decode('utf-8') # test read -# ok_list.append((filename, slug, title)) -# except UnicodeDecodeError: -# error_list.append((filename, title, _('File should be UTF-8 encoded.'))) -# slugs[slug] = filename -# -# if not error_list: -# for filename, slug, title in ok_list: -# book = Book.create( -# text=zip.read(filename).decode('utf-8'), -# creator=creator, -# slug=slug, -# title=title, -# ) -# -# return render(request, "catalogue/document_upload.html", { -# "form": form, -# "ok_list": ok_list, -# "skipped_list": skipped_list, -# "error_list": error_list, -# -# "logout_to": '/', -# }) -# else: -# form = forms.DocumentsUploadForm() -# -# return render(request, "catalogue/document_upload.html", { -# "form": form, -# -# "logout_to": '/', -# }) - - -# @never_cache -# def book_xml(request, slug): -# 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 -# return response - - -# @never_cache -# def book_txt(request, slug): -# book = get_object_or_404(Book, slug=slug) -# if not book.accessible(request): -# return HttpResponseForbidden("Not authorized.") -# -# doc = book.wldocument() -# text = doc.as_text().get_string() -# response = http.HttpResponse(text, content_type='text/plain', mimetype='text/plain') -# response['Content-Disposition'] = 'attachment; filename=%s.txt' % slug -# return response - - @never_cache def book_html(request, pk, rev_pk=None, preview=False): from librarian.document import Document as SST @@ -455,192 +340,6 @@ def book_delete(request, pk): }) -# 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.BookForm(request.POST, instance=book) -# if form.is_valid(): -# form.save() -# return http.HttpResponseRedirect(book.get_absolute_url()) -# else: -# form = forms.BookForm(instance=book) -# editable = True -# else: -# form = forms.ReadonlyBookForm(instance=book) -# editable = False -# -# publish_error = book.publishable_error() -# publishable = publish_error is None -# -# return render(request, "catalogue/book_detail.html", { -# "book": book, -# "publishable": publishable, -# "publishable_error": publish_error, -# "form": form, -# "editable": editable, -# }) - - -# @permission_required('catalogue.add_chunk') -# def chunk_add(request, slug, chunk): -# try: -# 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) -# if form.is_valid(): -# if request.user.is_authenticated(): -# creator = request.user -# else: -# creator = None -# doc.split(creator=creator, -# slug=form.cleaned_data['slug'], -# title=form.cleaned_data['title'], -# gallery_start=form.cleaned_data['gallery_start'], -# user=form.cleaned_data['user'], -# stage=form.cleaned_data['stage'] -# ) -# -# return http.HttpResponseRedirect(doc.book.get_absolute_url()) -# else: -# form = forms.ChunkAddForm(initial={ -# "slug": str(doc.number + 1), -# "title": "cz. %d" % (doc.number + 1, ), -# }) -# -# return render(request, "catalogue/chunk_add.html", { -# "chunk": doc, -# "form": form, -# }) - - -# @login_required -# def chunk_edit(request, slug, chunk): -# try: -# 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(): -# form.save() -# go_next = request.GET.get('next', None) -# if go_next: -# go_next = urlquote_plus(unquote(iri_to_uri(go_next)), safe='/?=&') -# else: -# go_next = doc.book.get_absolute_url() -# return http.HttpResponseRedirect(go_next) -# else: -# form = forms.ChunkForm(instance=doc) -# -# referer = request.META.get('HTTP_REFERER') -# if referer: -# parts = urlsplit(referer) -# parts = ['', ''] + list(parts[2:]) -# go_next = urlquote_plus(urlunsplit(parts)) -# else: -# go_next = '' -# -# return render(request, "catalogue/chunk_edit.html", { -# "chunk": doc, -# "form": form, -# "go_next": go_next, -# }) - - -# @transaction.atomic -# @login_required -# def chunk_mass_edit(request): -# if request.method == 'POST': -# ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(','))) -# chunks = map(lambda i: Chunk.objects.get(id=i), ids) -# -# stage = request.POST.get('stage') -# if stage: -# try: -# stage = Chunk.tag_model.objects.get(slug=stage) -# except Chunk.DoesNotExist, e: -# stage = None -# -# for c in chunks: c.stage = stage -# -# username = request.POST.get('user') -# logger.info("username: %s" % username) -# logger.info(request.POST) -# if username: -# try: -# user = User.objects.get(username=username) -# except User.DoesNotExist, e: -# user = None -# -# for c in chunks: c.user = user -# -# status = request.POST.get('status') -# if status: -# books_affected = set() -# for c in chunks: -# if status == 'publish': -# c.head.publishable = True -# c.head.save() -# elif status == 'unpublish': -# c.head.publishable = False -# c.head.save() -# c.touch() # cache -# books_affected.add(c.book) -# for b in books_affected: -# b.touch() # cache -# -# project_id = request.POST.get('project') -# if project_id: -# try: -# project = Project.objects.get(pk=int(project_id)) -# except (Project.DoesNotExist, ValueError), e: -# project = None -# for c in chunks: -# book = c.book -# book.project = project -# book.save() -# -# for c in chunks: c.save() -# -# return HttpResponse("", content_type="text/plain") -# else: -# raise Http404 - - -# @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(): -# append_to = form.cleaned_data['append_to'] -# append_to.append(book) -# return http.HttpResponseRedirect(append_to.get_absolute_url()) -# else: -# form = forms.BookAppendForm(book) -# return render(request, "catalogue/book_append_to.html", { -# "book": book, -# "form": form, -# -# "logout_to": '/', -# }) - - @require_POST @login_required def publish(request, pk): @@ -702,12 +401,6 @@ class GalleryView(GalleryMixin, UploadView): self.doc = Document.objects.get(pk=pk, deleted=False) -class GalleryPackageView(GalleryMixin, PackageView): - - def get_redirect_url(self, slug): - return reverse('catalogue_book_gallery', kwargs=dict(slug=slug)) - - @login_required def fork(request, pk): doc = get_object_or_404(Document, pk=pk, deleted=False) diff --git a/apps/catalogue/xml_tools.py b/apps/catalogue/xml_tools.py index 242714b6..4365fbdd 100644 --- a/apps/catalogue/xml_tools.py +++ b/apps/catalogue/xml_tools.py @@ -103,7 +103,7 @@ def split_xml(text): name_elem = deepcopy(element) for tag in 'extra', 'motyw', 'pa', 'pe', 'pr', 'pt', 'uwaga': for a in name_elem.findall('.//' + tag): - a.text='' + a.text = '' del a[:] name = etree.tostring(name_elem, method='text', encoding='utf-8').strip() @@ -123,15 +123,17 @@ def split_xml(text): while parent[0] is not element: del parent[0] element, parent = parent, parent.getparent() - chunks[:0] = [[name, + chunks[:0] = [[ + name, unicode(etree.tostring(copied, encoding='utf-8'), 'utf-8') - ]] + ]] parts = src.findall('.//naglowek_rozdzial') - chunks[:0] = [[u'początek', + chunks[:0] = [[ + u'początek', unicode(etree.tostring(src, encoding='utf-8'), 'utf-8') - ]] + ]] for ch in chunks[1:]: ch[1] = add_trim_begin(ch[1]) diff --git a/apps/cover/__init__.py b/apps/cover/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/cover/forms.py b/apps/cover/forms.py deleted file mode 100755 index 628cb63e..00000000 --- a/apps/cover/forms.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import re -from urllib2 import urlopen -from django import forms -from django.utils.translation import ugettext_lazy as _ -from cover.models import Image - -class ImageAddForm(forms.ModelForm): - class Meta: - model = Image - exclude = [] - - def __init__(self, *args, **kwargs): - super(ImageAddForm, self).__init__(*args, **kwargs) - self.fields['file'].required = self.fields['download_url'].required = self.fields['source_url'].required = False - - def clean_download_url(self): - return self.cleaned_data['download_url'] or None - - def clean_source_url(self): - return self.cleaned_data['source_url'] or None - - def clean(self): - cleaned_data = super(ImageAddForm, self).clean() - if not cleaned_data.get('download_url', None) and not cleaned_data.get('file', None): - raise forms.ValidationError('No image specified') - return cleaned_data - - -class ImageEditForm(forms.ModelForm): - """Form used for editing a Book.""" - class Meta: - model = Image - exclude = ['download_url'] - - -class ReadonlyImageEditForm(ImageEditForm): - """Form used for not editing a Book.""" - - def __init__(self, *args, **kwargs): - ret = super(ReadonlyImageEditForm, self).__init__(*args, **kwargs) - for field in self.fields.values(): - field.widget.attrs.update({"readonly": True}) - return ret - - def save(self, *args, **kwargs): - raise AssertionError, "ReadonlyImageEditForm should not be saved." - - -class FlickrForm(forms.Form): - source_url = forms.URLField(label=_('Flickr URL')) - - def clean_source_url(self): - def normalize_html(html): - return re.sub('[\t\n]', '', html) - - url = self.cleaned_data['source_url'] - m = re.match(r'(https?://)?(www\.|secure\.)?flickr\.com/photos/(?P[^/]+)/(?P\d+)/?', url) - if not m: - raise forms.ValidationError("It doesn't look like Flickr URL.") - author_slug, img_id = m.group('author'), m.group('img') - base_url = "https://www.flickr.com/photos/%s/%s/" % (author_slug, img_id) - - try: - html = normalize_html(urlopen(url).read().decode('utf-8')) - except: - raise forms.ValidationError('Error reading page.') - match = re.search(r'Some rights reserved', html) - try: - assert match - license_url = match.group(1) - self.cleaned_data['license_url'] = license_url - re_license = re.compile(r'http://creativecommons.org/licenses/([^/]*)/([^/]*)/.*') - m = re_license.match(license_url) - assert m - self.cleaned_data['license_name'] = 'CC %s %s' % (m.group(1).upper(), m.group(2)) - except AssertionError: - raise forms.ValidationError('Error reading license name.') - - m = re.search(r'"ownername":"([^"]*)', html) - if m: - self.cleaned_data['author'] = "%s@Flickr" % m.group(1) - else: - raise forms.ValidationError('Error reading author name.') - - m = re.search(r']*>(.*?)', html) - if not m: - raise forms.ValidationError('Error reading image title.') - self.cleaned_data['title'] = m.group(1) - - url_size = base_url + "sizes/o/" - html = normalize_html(urlopen(url_size).read().decode('utf-8')) - m = re.search(r'
\s*, YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-26 10:36+0100\n" -"PO-Revision-Date: 2012-06-18 12:38+0100\n" -"Last-Translator: Radek Czajka \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\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:55 -msgid "Flickr URL" -msgstr "URL z Flickra" - -#: models.py:19 -msgid "title" -msgstr "tytuł" - -#: models.py:20 -msgid "author" -msgstr "autor" - -#: models.py:21 -msgid "license name" -msgstr "nazwa licencji" - -#: models.py:22 -msgid "license URL" -msgstr "URL licencji" - -#: models.py:23 -msgid "source URL" -msgstr "URL źródła" - -#: models.py:24 -msgid "image download URL" -msgstr "URL pliku do pobrania" - -#: models.py:25 -msgid "file" -msgstr "plik" - -#: models.py:28 -msgid "cover image" -msgstr "obrazek na okładkę" - -#: models.py:29 -msgid "cover images" -msgstr "obrazki na okładki" - -#: templates/cover/add_image.html:33 templates/cover/add_image.html.py:62 -msgid "Add image" -msgstr "Dodaj obrazek" - -#: templates/cover/add_image.html:40 -msgid "Load from Flickr" -msgstr "Pobierz z Flickra" - -#: templates/cover/image_detail.html:7 -msgid "Cover image" -msgstr "Obrazek na okładkę" - -#: templates/cover/image_detail.html:23 -msgid "source" -msgstr "źródło" - -#: templates/cover/image_detail.html:31 -msgid "Change" -msgstr "Zmień" - -#: templates/cover/image_detail.html:37 -msgid "Used in:" -msgstr "Użyte w:" - -#: templates/cover/image_detail.html:45 -msgid "None" -msgstr "Brak" - -#: templates/cover/image_list.html:7 -msgid "Cover images" -msgstr "Obrazki na okładki" - -#: templates/cover/image_list.html:10 -msgid "Add new" -msgstr "Dodaj nowy" diff --git a/apps/cover/models.py b/apps/cover/models.py deleted file mode 100644 index d4432c2c..00000000 --- a/apps/cover/models.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import re -from urlparse import urljoin -from django.conf import settings -from django.core.files.base import ContentFile -from django.db import models -from django.db.models.signals import post_save -from django.dispatch import receiver -from django.utils.translation import ugettext_lazy as _ -from django.contrib.sites.models import Site -from cover.utils import URLOpener - - -class Image(models.Model): - title = models.CharField(max_length=255, verbose_name=_('title')) - author = models.CharField(max_length=255, verbose_name=_('author')) - license_name = models.CharField(max_length=255, verbose_name=_('license name')) - license_url = models.URLField(max_length=255, blank=True, verbose_name=_('license URL')) - source_url = models.URLField(verbose_name=_('source URL'), null = True) - download_url = models.URLField(unique=True, verbose_name=_('image download URL'), null = True) - file = models.ImageField(upload_to='cover/image', editable=True, verbose_name=_('file')) - - class Meta: - verbose_name = _('cover image') - verbose_name_plural = _('cover images') - - def __unicode__(self): - return u"%s - %s" % (self.author, self.title) - - @models.permalink - def get_absolute_url(self): - return ('cover_image', [self.id]) - - def get_full_url(self): - return "http://%s%s" % (Site.objects.get_current().domain, self.get_absolute_url()) - - -@receiver(post_save, sender=Image) -def download_image(sender, instance, **kwargs): - if instance.pk and not instance.file: - t = URLOpener().open(instance.download_url).read() - instance.file.save("%d.jpg" % instance.pk, ContentFile(t)) - - \ No newline at end of file diff --git a/apps/cover/templates/cover/add_image.html b/apps/cover/templates/cover/add_image.html deleted file mode 100755 index 1854555f..00000000 --- a/apps/cover/templates/cover/add_image.html +++ /dev/null @@ -1,67 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} - -{% block add_js %} - {{block.super}} - -{% endblock %} - -{% block content %} -

{% trans "Add image" %}

- - -
{% csrf_token %} - - - {{ ff.as_table }} - -
-
- -
{% csrf_token %} -{{ form.non_field_errors }} - - {% for field in form %} - {% if field.name != 'download_url' and field.name != 'file' %} - - - - - {% endif %} - {% endfor %} - - - - - - - - -
{{field.errors}} {{field.label}}{{field}}
{{ form.download_url.errors }} {{form.download_url.label}}{{form.download_url}}{{ form.file.errors }} Lub {{form.file.label}}{{form.file}}
-
- - -{% endblock %} diff --git a/apps/cover/templates/cover/image_detail.html b/apps/cover/templates/cover/image_detail.html deleted file mode 100755 index 5707b29e..00000000 --- a/apps/cover/templates/cover/image_detail.html +++ /dev/null @@ -1,54 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} -{% load thumbnail %} -{% load build_absolute_uri from common_tags %} - -{% block content %} -

{% trans "Cover image" %}

- -
- - - -
{{ object.title }} by {{ object.author }}, - {% if object.license_url %}{% endif %} - {{ object.license_name }} - {% if object.license_url %}{% endif %} - -
{% trans "source" %}: {{ object.download_url }} -
- - -{% if editable %}
{% csrf_token %}{% endif %} - - {{ form.as_table }} - {% if editable %} - - {% endif %} -
-{% if editable %}
{% endif %} - - -

{% trans "Used in:" %}

-{% if object.book_set %} -
    - {% for book in object.book_set.all %} -
  • {{ book }}
  • - {% endfor %} -
-{% else %} -

{% trans "None" %}

-{% endif %} - - - -{% endblock %} diff --git a/apps/cover/templates/cover/image_list.html b/apps/cover/templates/cover/image_list.html deleted file mode 100755 index 2b799fe5..00000000 --- a/apps/cover/templates/cover/image_list.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "catalogue/base.html" %} -{% load i18n %} -{% load thumbnail pagination_tags %} - - -{% block content %} -

{% trans "Cover images" %}

- -{% if can_add %} - {% trans "Add new" %} -{% endif %} - -
    -{% autopaginate object_list 100 %} -{% for image in object_list %} - - - -
    - {{ image }}
    -{% endfor %} -{% paginate %} -
- -{% endblock %} diff --git a/apps/cover/tests.py b/apps/cover/tests.py deleted file mode 100644 index 2f5b304e..00000000 --- a/apps/cover/tests.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from nose.tools import * -from django.test import TestCase -from cover.forms import FlickrForm - - -class FlickrTests(TestCase): - def test_flickr(self): - form = FlickrForm({"source_url": "https://www.flickr.com/photos/rczajka/6941928577/in/photostream"}) - self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['source_url'], "https://www.flickr.com/photos/rczajka/6941928577/") - self.assertEqual(form.cleaned_data['author'], "rczajka@Flickr") - self.assertEqual(form.cleaned_data['title'], u"Pirate Stańczyk") - self.assertEqual(form.cleaned_data['license_name'], "CC BY 2.0") - self.assertEqual(form.cleaned_data['license_url'], "http://creativecommons.org/licenses/by/2.0/deed.en") - self.assertEqual(form.cleaned_data['download_url'], "https://farm8.staticflickr.com/7069/6941928577_415844c58e_o.jpg") diff --git a/apps/cover/urls.py b/apps/cover/urls.py deleted file mode 100644 index f1a48d35..00000000 --- a/apps/cover/urls.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from django.conf.urls import patterns, url - - -urlpatterns = patterns('cover.views', - url(r'^preview/$', 'preview_from_xml', name='cover_preview'), - url(r'^preview/(?P[^/]+)/$', 'preview', name='cover_preview'), - url(r'^preview/(?P[^/]+)/(?P[^/]+)/$', - 'preview', name='cover_preview'), - url(r'^preview/(?P[^/]+)/(?P[^/]+)/(?P\d+)/$', - 'preview', name='cover_preview'), - - url(r'^image/$', 'image_list', name='cover_image_list'), - url(r'^image/(?P\d+)/?$', 'image', name='cover_image'), - url(r'^add_image/$', 'add_image', name='cover_add_image'), -) diff --git a/apps/cover/utils.py b/apps/cover/utils.py deleted file mode 100755 index e22fa727..00000000 --- a/apps/cover/utils.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -from urllib import FancyURLopener -from django.contrib.sites.models import Site - - -class URLOpener(FancyURLopener): - @property - def version(self): - return 'FNP Redakcja (http://%s)' % Site.objects.get_current() diff --git a/apps/cover/views.py b/apps/cover/views.py deleted file mode 100644 index 752a8244..00000000 --- a/apps/cover/views.py +++ /dev/null @@ -1,139 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. -# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. -# -import os.path -from django.conf import settings -from django.contrib.auth.decorators import permission_required -from django.http import HttpResponse, HttpResponseRedirect, Http404 -from django.shortcuts import get_object_or_404, render -from django.views.decorators.csrf import csrf_exempt -from django.views.decorators.http import require_POST -from catalogue.helpers import active_tab -from catalogue.models import Document -from cover.models import Image -from cover import forms - - -PREVIEW_SIZE = (216, 300) - - -def preview(request, book, chunk=None, rev=None): - """Creates a cover image. - - If chunk and rev number are given, use version from given revision. - If rev is not given, use publishable version. - """ - from PIL import Image - from librarian.cover import WLCover - from librarian.dcparser import BookInfo - - chunk = Chunk.get(book, chunk) - if rev is not None: - try: - revision = chunk.at_revision(rev) - except Chunk.change_model.DoesNotExist: - raise Http404 - else: - revision = chunk.publishable() - if revision is None: - raise Http404 - xml = revision.materialize().encode('utf-8') - - try: - info = BookInfo.from_string(xml) - except: - return HttpResponseRedirect(os.path.join(settings.STATIC_URL, "img/sample_cover.png")) - cover = WLCover(info) - response = HttpResponse(mimetype=cover.mime_type()) - image = cover.image().resize(PREVIEW_SIZE, Image.ANTIALIAS) - image.save(response, cover.format) - return response - - -@csrf_exempt -@require_POST -def preview_from_xml(request): - from hashlib import sha1 - from PIL import Image - from os import makedirs - from lxml import etree - from librarian.cover import WLCover - from librarian.dcparser import BookInfo - - xml = request.POST['xml'] - try: - info = BookInfo.from_string(xml.encode('utf-8')) - except: - return HttpResponse(os.path.join(settings.STATIC_URL, "img/sample_cover.png")) - coverid = sha1(etree.tostring(info.to_etree())).hexdigest() - cover = WLCover(info) - - cover_dir = 'cover/preview' - try: - makedirs(os.path.join(settings.MEDIA_ROOT, cover_dir)) - except OSError: - pass - fname = os.path.join(cover_dir, "%s.%s" % (coverid, cover.ext())) - image = cover.image().resize(PREVIEW_SIZE, Image.ANTIALIAS) - image.save(os.path.join(settings.MEDIA_ROOT, fname)) - return HttpResponse(os.path.join(settings.MEDIA_URL, fname)) - - -@active_tab('cover') -def image(request, pk): - image = get_object_or_404(Image, pk=pk) - - if request.user.has_perm('cover.change_image'): - if request.method == "POST": - form = forms.ImageEditForm(request.POST, instance=image) - if form.is_valid(): - form.save() - return HttpResponseRedirect(image.get_absolute_url()) - else: - form = forms.ImageEditForm(instance=image) - editable = True - else: - form = forms.ReadonlyImageEditForm(instance=image) - editable = False - - return render(request, "cover/image_detail.html", { - "object": image, - "form": form, - "editable": editable, - }) - - -@active_tab('cover') -def image_list(request): - objects = Image.objects.all() - enable_add = request.user.has_perm('cover.add_image') - return render(request, "cover/image_list.html", { - 'object_list': Image.objects.all(), - 'can_add': request.user.has_perm('cover.add_image'), - }) - - -@permission_required('cover.add_image') -@active_tab('cover') -def add_image(request): - form = ff = None - if request.method == 'POST': - if request.POST.get('form_id') == 'flickr': - ff = forms.FlickrForm(request.POST) - if ff.is_valid(): - form = forms.ImageAddForm(ff.cleaned_data) - else: - form = forms.ImageAddForm(request.POST, request.FILES) - if form.is_valid(): - obj = form.save() - return HttpResponseRedirect(obj.get_absolute_url()) - if form is None: - form = forms.ImageAddForm() - if ff is None: - ff = forms.FlickrForm() - return render(request, 'cover/add_image.html', { - 'form': form, - 'ff': ff, - }) diff --git a/apps/dvcs/models.py b/apps/dvcs/models.py index 19e7d1e4..6ecb97ce 100644 --- a/apps/dvcs/models.py +++ b/apps/dvcs/models.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from __future__ import unicode_literals, print_function from datetime import datetime @@ -8,9 +13,7 @@ from tempfile import NamedTemporaryFile from django.conf import settings from django.core.files.base import ContentFile -from django.core.files.storage import FileSystemStorage -from django.db import models, transaction -from django.db.models.base import ModelBase +from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ @@ -31,32 +34,22 @@ class Revision(models.Model): Gzipped text of the document is stored in a file. """ - author = models.ForeignKey(settings.AUTH_USER_MODEL, - null=True, blank=True, verbose_name=_('author')) - author_name = models.CharField(_('author name'), max_length=128, - null=True, blank=True, - help_text=_("Used if author is not set.") - ) - author_email = models.CharField(_('author email'), max_length=128, - null=True, blank=True, - help_text=_("Used if author is not set.") - ) + author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, verbose_name=_('author')) + author_name = models.CharField( + _('author name'), max_length=128, null=True, blank=True, help_text=_("Used if author is not set.")) + author_email = models.CharField( + _('author email'), max_length=128, null=True, blank=True, help_text=_("Used if author is not set.")) # Any other author data? # How do we identify an author? - parent = models.ForeignKey('self', - null=True, blank=True, default=None, - verbose_name=_('parent'), - related_name="children") + parent = models.ForeignKey( + 'self', null=True, blank=True, default=None, verbose_name=_('parent'), related_name="children") - merge_parent = models.ForeignKey('self', - null=True, blank=True, default=None, - verbose_name=_('merge parent'), - related_name="merge_children") + merge_parent = models.ForeignKey( + 'self', null=True, blank=True, default=None, verbose_name=_('merge parent'), related_name="merge_children") description = models.TextField(_('description'), blank=True, default='') - created_at = models.DateTimeField(editable=False, db_index=True, - default=datetime.now) + created_at = models.DateTimeField(editable=False, db_index=True, default=datetime.now) class Meta: ordering = ('created_at',) @@ -68,7 +61,7 @@ class Revision(models.Model): def get_text_path(self): if self.pk: - return re.sub(r'([0-9a-f]{2})([^\.])', r'\1/\2', '%x.gz' % self.pk) + return re.sub(r'([0-9a-f]{2})([^.])', r'\1/\2', '%x.gz' % self.pk) else: return None @@ -88,9 +81,8 @@ class Revision(models.Model): ) @classmethod - def create(cls, text, parent=None, merge_parent=None, - author=None, author_name=None, author_email=None, - description=''): + def create(cls, text, parent=None, merge_parent=None, author=None, author_name=None, author_email=None, + description=''): if text: text = text.replace( @@ -168,15 +160,14 @@ class Revision(models.Model): revs.update(self.merge_parent.get_ancestors()) return revs + @python_2_unicode_compatible class Ref(models.Model): """A reference pointing to a specific revision.""" - revision = models.ForeignKey(Revision, - null=True, blank=True, default=None, - verbose_name=_('revision'), - help_text=_("The document's revision."), - editable=False) + revision = models.ForeignKey( + Revision, null=True, blank=True, default=None, verbose_name=_('revision'), + help_text=_("The document's revision."), editable=False) def __str__(self): return "ref:{0}->rev:{1}".format(self.id, self.revision_id) @@ -194,12 +185,9 @@ class Ref(models.Model): for f in files: os.unlink(f) - return result.decode('utf-8') - def merge_with(self, revision, - author=None, author_name=None, author_email=None, - description="Automatic merge."): + def merge_with(self, revision, author=None, author_name=None, author_email=None, description="Automatic merge."): """Merges a given revision into the ref.""" if self.revision is None: fast_forward = True @@ -238,17 +226,13 @@ class Ref(models.Model): def materialize(self): return self.revision.materialize() if self.revision is not None else '' - def commit(self, text, parent=False, - author=None, author_name=None, author_email=None, - description=''): + def commit(self, text, parent=False, author=None, author_name=None, author_email=None, description=''): """Creates a new revision and sets it as the ref. This will automatically merge the commit into the main branch, if parent is not document's head. :param unicode text: new version of the document - :param base: parent revision (head, if not specified) - :type base: Revision or None :param User author: the commiter :param unicode author_name: commiter name (if ``author`` not specified) :param unicode author_email: commiter e-mail (if ``author`` not specified) @@ -267,8 +251,7 @@ class Ref(models.Model): description=description, parent=parent ) - self.merge_with(rev, author=author, author_name=author_name, - author_email=author_email) + self.merge_with(rev, author=author, author_name=author_name, author_email=author_email) post_commit.send(sender=type(self), instance=self) diff --git a/apps/dvcs/signals.py b/apps/dvcs/signals.py index d71d655b..3aa87de7 100755 --- a/apps/dvcs/signals.py +++ b/apps/dvcs/signals.py @@ -1,6 +1,11 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from __future__ import unicode_literals from django.dispatch import Signal post_commit = Signal(providing_args=['instance']) -post_merge = Signal(providing_args=['fast_forward', 'instance']) \ No newline at end of file +post_merge = Signal(providing_args=['fast_forward', 'instance']) diff --git a/apps/dvcs/storage.py b/apps/dvcs/storage.py index 4bf292bc..080e7077 100755 --- a/apps/dvcs/storage.py +++ b/apps/dvcs/storage.py @@ -1,7 +1,11 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from __future__ import unicode_literals -import os -from django.core.files.base import ContentFile, File +from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage try: @@ -35,4 +39,4 @@ class GzipFileSystemStorage(FileSystemStorage): def get_available_name(self, name): if self.exists(name): self.delete(name) - return name \ No newline at end of file + return name diff --git a/apps/dvcs/tests/__init__.py b/apps/dvcs/tests/__init__.py index 868f00a3..0d0a2882 100755 --- a/apps/dvcs/tests/__init__.py +++ b/apps/dvcs/tests/__init__.py @@ -1,178 +1,5 @@ -from nose.tools import * -from django.test import TestCase -from dvcs.models import Document - - -class ADocument(Document): - class Meta: - app_label = 'dvcs' - - -class DocumentModelTests(TestCase): - - def assertTextEqual(self, given, expected): - return self.assertEqual(given, expected, - "Expected '''%s'''\n differs from text: '''%s'''" % (expected, given) - ) - - def test_empty_file(self): - doc = ADocument.objects.create() - self.assertTextEqual(doc.materialize(), u"") - - def test_single_commit(self): - doc = ADocument.objects.create() - doc.commit(text=u"Ala ma kota", description="Commit #1") - self.assertTextEqual(doc.materialize(), u"Ala ma kota") - - def test_chained_commits(self): - doc = ADocument.objects.create() - text1 = u""" - Line #1 - Line #2 is cool - """ - text2 = u""" - Line #1 - Line #2 is hot - """ - text3 = u""" - Line #1 - ... is hot - Line #3 ate Line #2 - """ - - c1 = doc.commit(description="Commit #1", text=text1) - c2 = doc.commit(description="Commit #2", text=text2) - c3 = doc.commit(description="Commit #3", text=text3) - - self.assertTextEqual(doc.materialize(), text3) - self.assertTextEqual(doc.materialize(change=c3), text3) - self.assertTextEqual(doc.materialize(change=c2), text2) - self.assertTextEqual(doc.materialize(change=c1), text1) - - def test_parallel_commit_noconflict(self): - doc = ADocument.objects.create() - text1 = u""" - Line #1 - Line #2 - """ - text2 = u""" - Line #1 is hot - Line #2 - """ - text3 = u""" - Line #1 - Line #2 - Line #3 - """ - text_merged = u""" - Line #1 is hot - Line #2 - Line #3 - """ - - base = doc.commit(description="Commit #1", text=text1) - c1 = doc.commit(description="Commit #2", text=text2) - commits = doc.change_set.count() - c2 = doc.commit(description="Commit #3", text=text3, parent=base) - self.assertEqual(doc.change_set.count(), commits + 2, - u"Parallel commits should create an additional merge commit") - self.assertTextEqual(doc.materialize(), text_merged) - - def test_parallel_commit_conflict(self): - doc = ADocument.objects.create() - text1 = u""" - Line #1 - Line #2 - Line #3 - """ - text2 = u""" - Line #1 - Line #2 is hot - Line #3 - """ - text3 = u""" - Line #1 - Line #2 is cool - Line #3 - """ - text_merged = u""" - Line #1 -<<<<<<< - Line #2 is hot -======= - Line #2 is cool ->>>>>>> - Line #3 - """ - base = doc.commit(description="Commit #1", text=text1) - c1 = doc.commit(description="Commit #2", text=text2) - commits = doc.change_set.count() - c2 = doc.commit(description="Commit #3", text=text3, parent=base) - self.assertEqual(doc.change_set.count(), commits + 2, - u"Parallel commits should create an additional merge commit") - self.assertTextEqual(doc.materialize(), text_merged) - - - def test_multiple_parallel_commits(self): - text_a1 = u""" - Line #1 - - Line #2 - - Line #3 - """ - text_a2 = u""" - Line #1 * - - Line #2 - - Line #3 - """ - text_b1 = u""" - Line #1 - - Line #2 ** - - Line #3 - """ - text_c1 = u""" - Line #1 - - Line #2 - - Line #3 *** - """ - text_merged = u""" - Line #1 * - - Line #2 ** - - Line #3 *** - """ - - - doc = ADocument.objects.create() - c1 = doc.commit(description="Commit A1", text=text_a1) - c2 = doc.commit(description="Commit A2", text=text_a2, parent=c1) - c3 = doc.commit(description="Commit B1", text=text_b1, parent=c1) - c4 = doc.commit(description="Commit C1", text=text_c1, parent=c1) - self.assertTextEqual(doc.materialize(), text_merged) - - - def test_prepend_history(self): - doc1 = ADocument.objects.create() - doc2 = ADocument.objects.create() - doc1.commit(text='Commit 1') - doc2.commit(text='Commit 2') - doc2.prepend_history(doc1) - self.assertEqual(ADocument.objects.all().count(), 1) - self.assertTextEqual(doc2.at_revision(1).materialize(), 'Commit 1') - self.assertTextEqual(doc2.materialize(), 'Commit 2') - - def test_prepend_to_self(self): - doc = ADocument.objects.create() - doc.commit(text='Commit 1') - with self.assertRaises(AssertionError): - doc.prepend_history(doc) - self.assertTextEqual(doc.materialize(), 'Commit 1') - +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# diff --git a/apps/dvcs/version.py b/apps/dvcs/version.py index bb786ec5..729bd3d9 100644 --- a/apps/dvcs/version.py +++ b/apps/dvcs/version.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from __future__ import unicode_literals -VERSION='0.1' +VERSION = '0.1' diff --git a/apps/email_mangler/templatetags/email.py b/apps/email_mangler/templatetags/email.py index 376117a8..1d0080b6 100755 --- a/apps/email_mangler/templatetags/email.py +++ b/apps/email_mangler/templatetags/email.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _ @@ -17,7 +22,8 @@ def email_link(email): at = escape(_('at')) dot = escape(_('dot')) mangled = "%s %s %s" % (name, at, (' %s ' % dot).join(domain.split('.'))) - return mark_safe("%(mangled)s" % { 'name': name.encode('rot13'), 'domain': domain.encode('rot13'), diff --git a/apps/fileupload/forms.py b/apps/fileupload/forms.py index f5e10699..d4fbd49d 100644 --- a/apps/fileupload/forms.py +++ b/apps/fileupload/forms.py @@ -1,4 +1,10 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django import forms + class UploadForm(forms.Form): files = forms.FileField() diff --git a/apps/fileupload/models.py b/apps/fileupload/models.py deleted file mode 100644 index 8b137891..00000000 --- a/apps/fileupload/models.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/fileupload/templatetags/upload_tags.py b/apps/fileupload/templatetags/upload_tags.py index aefce2e3..e49566fb 100644 --- a/apps/fileupload/templatetags/upload_tags.py +++ b/apps/fileupload/templatetags/upload_tags.py @@ -1,7 +1,13 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django import template register = template.Library() + @register.simple_tag def upload_js(): return """ @@ -13,10 +19,13 @@ def upload_js(): {%=file.name%} {%=o.formatFileSize(file.size)%} {% if (file.error) { %} - {%=locale.fileupload.error%} {%=locale.fileupload.errors[file.error] || file.error%} + {%=locale.fileupload.error%} + {%=locale.fileupload.errors[file.error] || file.error%} {% } else if (o.files.valid && !i) { %} -
+
+
+
{% if (!o.options.autoUpload) { %} - {% trans "Save attempt in progress" %} - {% trans "There is a newer version of this document!" %} - {% endif %} -
-
    - {% block tabs-menu %} {% endblock %} -
-
    - {% block tabs-right %} {% endblock %} -
- -
-
- {% block tabs-content %} {% endblock %} -
- {% block splitter-extra %} {% endblock %} -
- -{% block dialogs %} {% endblock %} - -{% endblock %} diff --git a/apps/wiki/templates/wiki/document_details_readonly.html b/apps/wiki/templates/wiki/document_details_readonly.html deleted file mode 100644 index 71556a19..00000000 --- a/apps/wiki/templates/wiki/document_details_readonly.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "wiki/document_details_base.html" %} -{% load i18n %} - -{% block editor-class %}readonly{% endblock %} - -{% block extrabody %} -{{ block.super }} - - -{% endblock %} - -{% block tabs-menu %} - {% include "wiki/tabs/wysiwyg_editor_item.html" %} - {% include "wiki/tabs/source_editor_item.html" %} -{% endblock %} - -{% block tabs-content %} - {% include "wiki/tabs/wysiwyg_editor.html" %} - {% include "wiki/tabs/source_editor.html" %} -{% endblock %} - -{% block splitter-extra %} -{% endblock %} - -{% block dialogs %} -{% endblock %} \ No newline at end of file diff --git a/apps/wiki/templates/wiki/tabs/history_view.html b/apps/wiki/templates/wiki/tabs/history_view.html deleted file mode 100644 index 93b0bb64..00000000 --- a/apps/wiki/templates/wiki/tabs/history_view.html +++ /dev/null @@ -1,41 +0,0 @@ -{% load i18n %} - diff --git a/apps/wiki/templates/wiki/tabs/history_view_item.html b/apps/wiki/templates/wiki/tabs/history_view_item.html deleted file mode 100644 index bf39a333..00000000 --- a/apps/wiki/templates/wiki/tabs/history_view_item.html +++ /dev/null @@ -1,4 +0,0 @@ -{% load i18n %} -
  • - {% trans "History" %} -
  • diff --git a/apps/wiki/templates/wiki/tabs/summary_view.html b/apps/wiki/templates/wiki/tabs/summary_view.html deleted file mode 100644 index 8a57769e..00000000 --- a/apps/wiki/templates/wiki/tabs/summary_view.html +++ /dev/null @@ -1,44 +0,0 @@ -{% load i18n %} - diff --git a/apps/wiki/templates/wiki/tabs/summary_view_item.html b/apps/wiki/templates/wiki/tabs/summary_view_item.html deleted file mode 100644 index 856b3d70..00000000 --- a/apps/wiki/templates/wiki/tabs/summary_view_item.html +++ /dev/null @@ -1,4 +0,0 @@ -{% load i18n %} -
  • - {% trans "Summary" %} -
  • diff --git a/apps/wiki/urls.py b/apps/wiki/urls.py index ee13b850..69472103 100644 --- a/apps/wiki/urls.py +++ b/apps/wiki/urls.py @@ -2,22 +2,17 @@ from django.conf.urls import patterns, url -urlpatterns = patterns('wiki.views', +urlpatterns = patterns( + 'wiki.views', url(r'^edit/(?P[^/]+)/$', 'editor', name="wiki_editor"), - url(r'^readonly/(?P[^/]+)/$', - 'editor_readonly', name="wiki_editor_readonly"), - url(r'^gallery/(?P[^/]+)/$', 'gallery', name="wiki_gallery"), url(r'^history/(?P\d+)/$', 'history', name="wiki_history"), - url(r'^rev/(?P\d+)/$', - 'revision', name="wiki_revision"), - url(r'^text/(?P\d+)/$', 'text', name="wiki_text"), diff --git a/apps/wiki/views.py b/apps/wiki/views.py index 9a17faab..1b16077f 100644 --- a/apps/wiki/views.py +++ b/apps/wiki/views.py @@ -1,28 +1,29 @@ -from datetime import datetime +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# import json import os import logging import urllib from django.conf import settings -from django.core.urlresolvers import reverse from django import http -from django.http import Http404, HttpResponseForbidden +from django.http import HttpResponseForbidden from django.middleware.gzip import GZipMiddleware from django.utils.decorators import decorator_from_middleware from django.utils.encoding import smart_unicode from django.utils.formats import localize from django.utils.translation import ugettext as _ -from django.views.decorators.http import require_POST, require_GET +from django.views.decorators.http import require_POST from django.shortcuts import get_object_or_404, render -from django.contrib.auth.decorators import login_required from catalogue.models import Document, Template from dvcs.models import Revision import nice_diff from wiki import forms -from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError, - ajax_require_permission) +from wiki.helpers import JSONResponse, JSONFormInvalid # # Quick hack around caching problems, TODO: use ETags @@ -38,38 +39,25 @@ def get_history(document): revisions = [] for i, revision in enumerate(document.history()): revisions.append({ - "version": i + 1, - "description": revision.description, - "author": revision.author_str(), - "date": localize(revision.created_at), - "published": "", - "revision": revision.pk, - "published": _("Published") + ": " + \ - localize(revision.publish_log.order_by('-timestamp')[0].timestamp) \ - if revision.publish_log.exists() else "", - }) + "version": i + 1, + "description": revision.description, + "author": revision.author_str(), + "date": localize(revision.created_at), + "revision": revision.pk, + "published": _("Published") + ": " + + localize(revision.publish_log.order_by('-timestamp')[0].timestamp) + if revision.publish_log.exists() else "", + }) return revisions @never_cache -#@login_required -def editor(request, pk, chunk=None, template_name='wiki/bootstrap.html'): +def editor(request, pk, template_name='wiki/bootstrap.html'): doc = get_object_or_404(Document, pk=pk, deleted=False) - #~ if not doc.accessible(request): - #~ return HttpResponseForbidden("Not authorized.") - - access_time = datetime.now() save_form = forms.DocumentTextSaveForm(user=request.user, prefix="textsave") - try: - version = int(request.GET.get('version', None)) - except: - version = None - if version: - text = doc.at_revision(version).materialize() - else: - text = doc.materialize() - revision = doc.revision + text = doc.materialize() + revision = doc.revision history = get_history(doc) return render(request, template_name, { 'serialized_document_data': json.dumps({ @@ -77,7 +65,7 @@ def editor(request, pk, chunk=None, template_name='wiki/bootstrap.html'): 'document_id': doc.pk, 'title': doc.meta().get('title', ''), 'history': history, - 'version': len(history), #version or chunk.revision(), + 'version': len(history), 'revision': revision.pk, 'stage': doc.stage, 'assignment': str(doc.assigned_to), @@ -94,42 +82,12 @@ def editor(request, pk, chunk=None, template_name='wiki/bootstrap.html'): }) -@require_GET -def editor_readonly(request, slug, chunk=None, template_name='wiki/document_details_readonly.html'): - try: - chunk = Chunk.get(slug, chunk) - 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", {}) - last_books[slug, chunk.slug] = { - 'time': access_time, - 'title': chunk.book.title, - } - - if len(last_books) > MAX_LAST_DOCS: - oldest_key = min(last_books, key=lambda x: last_books[x]['time']) - del last_books[oldest_key] - request.session['wiki_last_books'] = last_books - - return render(request, template_name, { - 'chunk': chunk, - 'revision': revision, - 'readonly': True, - 'REDMINE_URL': settings.REDMINE_URL, - }) - - @never_cache @decorator_from_middleware(GZipMiddleware) def text(request, doc_id): doc = get_object_or_404(Document, pk=doc_id, deleted=False) - #~ if not doc.book.accessible(request): - #~ return HttpResponseForbidden("Not authorized.") + # if not doc.book.accessible(request): + # return HttpResponseForbidden("Not authorized.") if request.method == 'POST': form = forms.DocumentTextSaveForm(request.POST, user=request.user, prefix="textsave") @@ -139,33 +97,33 @@ def text(request, doc_id): else: author = None text = form.cleaned_data['text'] - #~ parent_revision = form.cleaned_data['parent_revision'] - #~ if parent_revision is not None: - #~ parent = doc.at_revision(parent_revision) - #~ else: - #~ parent = None + # parent_revision = form.cleaned_data['parent_revision'] + # if parent_revision is not None: + # parent = doc.at_revision(parent_revision) + # else: + # parent = None stage = form.cleaned_data['stage'] - #~ tags = [stage] if stage else [] - #~ publishable = (form.cleaned_data['publishable'] and - #~ request.user.has_perm('catalogue.can_pubmark')) + # tags = [stage] if stage else [] + # publishable = (form.cleaned_data['publishable'] and + # request.user.has_perm('catalogue.can_pubmark')) try: - doc.commit(author=author, - text=text, - parent=False, - description=form.cleaned_data['comment'], - author_name=form.cleaned_data['author_name'], - author_email=form.cleaned_data['author_email'], - ) + doc.commit( + author=author, + text=text, + description=form.cleaned_data['comment'], + author_name=form.cleaned_data['author_name'], + author_email=form.cleaned_data['author_email'], + ) doc.set_stage(stage) except: from traceback import print_exc print_exc() raise - #revision = doc.revision() + # revision = doc.revision() return JSONResponse({ - 'text': None, #doc.materialize() if parent_revision != revision else None, - #'version': revision, - #'stage': doc.stage.name if doc.stage else None, + 'text': None, # doc.materialize() if parent_revision != revision else None, + # 'version': revision, + # 'stage': doc.stage.name if doc.stage else None, 'assignment': doc.assigned_to.username if doc.assigned_to else None }) else: @@ -206,20 +164,20 @@ def revert(request, doc_id): else: author = None - #before = doc.revision + # before = doc.revision logger.info("Reverting %s to %s", doc_id, rev.pk) - doc.commit(author=author, - text=rev.materialize(), - parent=False, #? - description=comment, - #author_name=form.cleaned_data['author_name'], #? - #author_email=form.cleaned_data['author_email'], #? - ) + doc.commit( + author=author, + text=rev.materialize(), + description=comment, + # author_name=form.cleaned_data['author_name'], #? + # author_email=form.cleaned_data['author_email'], #? + ) return JSONResponse({ - #'document': None, #doc.materialize() if before != doc.revision else None, - #'version': doc.revision(), + # 'document': None, #doc.materialize() if before != doc.revision else None, + # 'version': doc.revision(), }) else: return JSONFormInvalid(form) @@ -278,16 +236,7 @@ def diff(request, doc_id): docA = "" docB = Revision.objects.get(pk=revB).materialize() - return http.HttpResponse(nice_diff.html_diff_table(docA.splitlines(), - docB.splitlines(), context=3)) - - -@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())) + return http.HttpResponse(nice_diff.html_diff_table(docA.splitlines(), docB.splitlines(), context=3)) @never_cache diff --git a/deployment.py b/deployment.py index b989fc7a..e478a47d 100644 --- a/deployment.py +++ b/deployment.py @@ -1,13 +1,17 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from __future__ import with_statement -import shutil import os import sys import logging +from string import Template logging.basicConfig(stream=sys.stderr, format="%(levelname)s:: %(message)s", level=logging.INFO) -from string import Template class DeploySite(object): @@ -92,6 +96,7 @@ class DeploySite(object): site = cls(*args, **kwargs) return site.deploy() + class WSGISite(DeploySite): def __init__(self, **env): @@ -99,7 +104,7 @@ class WSGISite(DeploySite): if 'WSGI_FILE' not in self.env: self.env['WSGI_FILE'] = os.path.join(self.env['ROOT'], 'www', - 'wsgi', self.env['PROJECT_NAME']) + '.wsgi' + 'wsgi', self.env['PROJECT_NAME']) + '.wsgi' self.env['WSGI_DIR'] = os.path.dirname(self.env['WSGI_FILE']) @@ -119,6 +124,7 @@ class WSGISite(DeploySite): source = self.find_resource(self.env['WSGI_SOURCE_FILE']) self.render_template(source, self.env['WSGI_FILE']) + class PIPSite(DeploySite): def install_dependencies(self): @@ -131,12 +137,14 @@ class PIPSite(DeploySite): except ValueError: pass + class GitSite(DeploySite): def update_app(self): self.info("Updating repository.") os.system("cd %s; git pull" % self.env['APP_DIR']) + class ApacheSite(DeploySite): def __init__(self, **env): diff --git a/lib/librarian b/lib/librarian index 25af49b6..d1037d61 160000 --- a/lib/librarian +++ b/lib/librarian @@ -1 +1 @@ -Subproject commit 25af49b6c63e1c005129856e107143864ad5b245 +Subproject commit d1037d617d8cd2cafc60e67ac0272f86d2e24dfa diff --git a/redakcja/context_processors.py b/redakcja/context_processors.py index 6ba41c6b..8aaf6933 100644 --- a/redakcja/context_processors.py +++ b/redakcja/context_processors.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 + def settings(request): from django.conf import settings diff --git a/redakcja/forms.py b/redakcja/forms.py index 22f3bab6..16ad87e7 100644 --- a/redakcja/forms.py +++ b/redakcja/forms.py @@ -1,5 +1,11 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from django import forms + class RegistrationForm(forms.Form): first_name = forms.CharField() last_name = forms.CharField() diff --git a/redakcja/settings/__init__.py b/redakcja/settings/__init__.py index 4d4fe8fb..2d04c8dc 100644 --- a/redakcja/settings/__init__.py +++ b/redakcja/settings/__init__.py @@ -1,11 +1,16 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from __future__ import absolute_import from os import path from redakcja.settings.common import * DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': path.join(PROJECT_ROOT, 'dev.sqlite'), # Or path to database file if using sqlite3. + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': path.join(PROJECT_ROOT, 'dev.sqlite'), 'USER': '', # Not used with sqlite3. 'PASSWORD': '', # Not used with sqlite3. 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. @@ -13,11 +18,7 @@ DATABASES = { } } -try: - LOGGING_CONFIG_FILE -except NameError: - LOGGING_CONFIG_FILE = os.path.join(PROJECT_ROOT, 'config', - ('logging.cfg' if not DEBUG else 'logging.cfg.dev')) +LOGGING_CONFIG_FILE = os.path.join(PROJECT_ROOT, 'config', ('logging.cfg' if not DEBUG else 'logging.cfg.dev')) try: import logging diff --git a/redakcja/settings/common.py b/redakcja/settings/common.py index b5e7f097..c32b6ad9 100644 --- a/redakcja/settings/common.py +++ b/redakcja/settings/common.py @@ -26,8 +26,8 @@ TIME_ZONE = 'Europe/Warsaw' # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = 'en' -#import locale -#locale.setlocale(locale.LC_ALL, '') +# import locale +# locale.setlocale(locale.LC_ALL, '') SITE_ID = 1 @@ -71,7 +71,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( "django.contrib.auth.context_processors.auth", "django.core.context_processors.debug", "django.core.context_processors.i18n", - "redakcja.context_processors.settings", # this is instead of media + "redakcja.context_processors.settings", # this is instead of media 'django.core.context_processors.csrf', "django.core.context_processors.request", ) @@ -85,18 +85,18 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.locale.LocaleMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - #'django_cas.middleware.CASMiddleware', + # 'django_cas.middleware.CASMiddleware', 'django.contrib.admindocs.middleware.XViewMiddleware', 'pagination.middleware.PaginationMiddleware', - #'maintenancemode.middleware.MaintenanceModeMiddleware', + # 'maintenancemode.middleware.MaintenanceModeMiddleware', 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', ) -#AUTHENTICATION_BACKENDS = ( -# 'django.contrib.auth.backends.ModelBackend', -# 'fnpdjango.auth_backends.AttrCASBackend', -#) +# AUTHENTICATION_BACKENDS = ( +# 'django.contrib.auth.backends.ModelBackend', +# 'fnpdjango.auth_backends.AttrCASBackend', +# ) ROOT_URLCONF = 'redakcja.urls' @@ -116,24 +116,24 @@ INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.admindocs', 'django.contrib.flatpages', - #'django.contrib.comments', + # 'django.contrib.comments', - #'south', + # 'south', 'sorl.thumbnail', 'pagination', - #'gravatar', - #'kombu.transport.django', + # 'gravatar', + # 'kombu.transport.django', 'fileupload', 'pipeline', 'modeltranslation', 'catalogue', - 'cover', + # 'cover', 'dvcs', 'organizations', 'wiki', - #'toolbar', - #'apiclient', + # 'toolbar', + # 'apiclient', 'email_mangler', 'build', 'attachments', @@ -145,16 +145,16 @@ INSTALLED_APPS = ( LOGIN_REDIRECT_URL = '/' -#CAS_USER_ATTRS_MAP = { -# 'email': 'email', 'firstname': 'first_name', 'lastname': 'last_name'} +# CAS_USER_ATTRS_MAP = { +# 'email': 'email', 'firstname': 'first_name', 'lastname': 'last_name'} # REPOSITORY_PATH = '/Users/zuber/Projekty/platforma/files/books' IMAGE_DIR = 'images/' -#import djcelery -#djcelery.setup_loader() +# import djcelery +# djcelery.setup_loader() BROKER_BACKEND = "djkombu.transport.DatabaseTransport" BROKER_HOST = "localhost" @@ -170,9 +170,7 @@ FORMS_BUILDER_EDITABLE_SLUGS = True FORMS_BUILDER_LABEL_MAX_LENGTH = 2048 - try: from redakcja.settings.compress import * except ImportError: pass - diff --git a/redakcja/settings/compress.py b/redakcja/settings/compress.py index 05b3b15a..decfa166 100644 --- a/redakcja/settings/compress.py +++ b/redakcja/settings/compress.py @@ -1,7 +1,12 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -# 'django.contrib.staticfiles.finders.DefaultStorageFinder', + # 'django.contrib.staticfiles.finders.DefaultStorageFinder', ) @@ -14,7 +19,7 @@ PIPELINE_STORAGE = 'pipeline.storage.PipelineFinderStorage' # CSS and JS files to compress PIPELINE_CSS = { 'detail': { - 'source_filenames': ( + 'source_filenames': ( 'css/master.css', 'css/toolbar.css', 'css/gallery.css', @@ -28,13 +33,13 @@ PIPELINE_CSS = { }, 'catalogue': { 'source_filenames': ( - #'css/filelist.css', + # 'css/filelist.css', 'css/base.css', 'datepicker/css/datepicker.css', ), 'output_filename': 'compressed/catalogue_styles.css', - }, - 'book': { + }, + 'book': { 'source_filenames': ( 'css/book.css', ), @@ -94,8 +99,8 @@ PIPELINE_JS = { 'datepicker/js/bootstrap-datepicker.js', ), 'output_filename': 'compressed/catalogue_scripts.js', - }, - 'book': { + }, + 'book': { 'source_filenames': ( 'js/book_text/jquery.eventdelegation.js', 'js/book_text/jquery.scrollto.js', diff --git a/redakcja/settings/integration_test.py b/redakcja/settings/integration_test.py index ba477bb0..36939319 100644 --- a/redakcja/settings/integration_test.py +++ b/redakcja/settings/integration_test.py @@ -1,7 +1,11 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# from redakcja.settings.test import * NOSE_ARGS = () STATIC_ROOT_SYMLINK = os.path.dirname(STATIC_ROOT) + '_test' STATICFILES_DIRS.append(STATIC_ROOT_SYMLINK) - diff --git a/redakcja/settings/test.py b/redakcja/settings/test.py index d667ddd7..832e2c0e 100644 --- a/redakcja/settings/test.py +++ b/redakcja/settings/test.py @@ -1,15 +1,21 @@ +# -*- coding: utf-8 -*- +# +# This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# # # Nose tests # from redakcja.settings.common import * +import tempfile # ROOT_URLCONF = 'yourapp.settings.test.urls' DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': '', # Or path to database file if using sqlite3. + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': '', # Or path to database file if using sqlite3. 'USER': '', # Not used with sqlite3. 'PASSWORD': '', # Not used with sqlite3. 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. @@ -17,7 +23,6 @@ DATABASES = { } } -import tempfile CATALOGUE_REPO_PATH = tempfile.mkdtemp(prefix='redakcja-repo') MEDIA_ROOT = tempfile.mkdtemp(prefix='media-root') @@ -26,8 +31,8 @@ USE_CELERY = False INSTALLED_APPS += ('django_nose', 'dvcs.tests') TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' -TEST_MODULES = ('catalogue', 'cover', 'dvcs.tests', 'wiki', 'toolbar') -COVER_APPS = ('catalogue', 'cover', 'dvcs', 'wiki', 'toolbar') +TEST_MODULES = ('catalogue', 'dvcs.tests', 'wiki', 'toolbar') +COVER_APPS = ('catalogue', 'dvcs', 'wiki', 'toolbar') NOSE_ARGS = ( '--tests=' + ','.join(TEST_MODULES), '--cover-package=' + ','.join(COVER_APPS), diff --git a/redakcja/static/css/html.css b/redakcja/static/css/html.css index 0d43611b..50a3e024 100644 --- a/redakcja/static/css/html.css +++ b/redakcja/static/css/html.css @@ -198,8 +198,7 @@ } /* błędne wersy */ -.htmlview *: -not(.strofa) > *[x-verse]::after { +.htmlview *:not(.strofa) > *[x-verse]::after { content: "Ten wers znajduje się poza strofą."; display: inline; background: red; @@ -418,7 +417,7 @@ div[x-node] > .uwaga { background-color: #ffcccc; } .htmlview .pe .annotation:hover { - background-color: #96e0e4;*/ + background-color: #96e0e4; } *.htmlview *.annotation-inline-box { position: static; diff --git a/redakcja/static/js/wiki/loader.js b/redakcja/static/js/wiki/loader.js deleted file mode 100644 index f2d7fb71..00000000 --- a/redakcja/static/js/wiki/loader.js +++ /dev/null @@ -1,151 +0,0 @@ -if (!window.console) { - window.console = { - log: function(){ - } - } -} - -var DEFAULT_PERSPECTIVE = "#VisualPerspective"; - -$(function() -{ - var tabs = $('ol#tabs li'); - var gallery = null; - CurrentDocument = new $.wikiapi.WikiDocument("document-meta"); - - $.blockUI.defaults.baseZ = 10000; - - function initialize() - { - $(document).keydown(function(event) { - console.log("Received key:", event); - }); - - /* The save button */ - $('#save-button').click(function(event){ - event.preventDefault(); - $.wiki.showDialog('#save_dialog'); - }); - - $('.editor').hide(); - - /* - * TABS - */ - $('.tabs li').live('click', function(event, callback) { - $.wiki.switchToTab(this); - }); - - $('#tabs li > .tabclose').live('click', function(event, callback) { - var $tab = $(this).parent(); - - if($tab.is('.active')) - $.wiki.switchToTab(DEFAULT_PERSPECTIVE); - - var p = $.wiki.perspectiveForTab($tab); - p.destroy(); - - return false; - }); - - - $(window).resize(function(){ - $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight()); - }); - - $(window).resize(); - - $('.vsplitbar').toggle( - function() { - $.wiki.state.perspectives.ScanGalleryPerspective.show = true; - $('#sidebar').show(); - $('.vsplitbar').css('right', 480).addClass('active'); - $('#editor .editor').css('right', 510); - $(window).resize(); - $.wiki.perspectiveForTab('#tabs-right .active').onEnter(); - }, - function() { - var active_right = $.wiki.perspectiveForTab('#tabs-right .active'); - $.wiki.state.perspectives.ScanGalleryPerspective.show = false; - $('#sidebar').hide(); - $('.vsplitbar').css('right', 0).removeClass('active'); - $(".vsplitbar-title").html("↑ " + active_right.vsplitbar + " ↑"); - $('#editor .editor').css('right', 30); - $(window).resize(); - active_right.onExit(); - } - ); - - if($.wiki.state.perspectives.ScanGalleryPerspective.show){ - $('.vsplitbar').trigger('click'); - $(".vsplitbar-title").html("↓ GALERIA ↓"); - } else { - $(".vsplitbar-title").html("↑ GALERIA ↑"); - } - window.onbeforeunload = function(e) { - if($.wiki.isDirty()) { - e.returnValue = "Na stronie mogą być nie zapisane zmiany."; - return "Na stronie mogą być nie zapisane zmiany."; - }; - }; - - console.log("Fetching document's text"); - - $(document).bind('wlapi_document_changed', function(event, doc) { - try { - $('#document-revision').text(doc.revision); - } catch(e) { - console.log("Failed handler", e); - } - }); - - CurrentDocument.fetch({ - success: function(){ - console.log("Fetch success"); - $('#loading-overlay').fadeOut(); - var active_tab = document.location.hash || DEFAULT_PERSPECTIVE; - - if(active_tab == "#ScanGalleryPerspective") - active_tab = DEFAULT_PERSPECTIVE; - - console.log("Initial tab is:", active_tab) - $.wiki.switchToTab(active_tab); - - /* every 5 minutes check for a newer version */ - var revTimer = setInterval(function() { - CurrentDocument.checkRevision({outdated: function(){ - $('#header').addClass('out-of-date'); - clearInterval(revTimer); - }}); - }, 300000); - }, - failure: function() { - $('#loading-overlay').fadeOut(); - alert("FAILURE"); - } - }); - }; /* end of initialize() */ - - - /* Load configuration */ - $.wiki.loadConfig(); - - var initAll = function(a, f) { - if (a.length == 0) return f(); - - $.wiki.initTab({ - tab: a.pop(), - doc: CurrentDocument, - callback: function(){ - initAll(a, f); - } - }); - }; - - - /* - * Initialize all perspectives - */ - initAll( $.makeArray($('.tabs li')), initialize); - console.log(location.hash); -}); diff --git a/redakcja/static/js/wiki/view_summary.js b/redakcja/static/js/wiki/view_summary.js index 099a0e81..55b9e53b 100644 --- a/redakcja/static/js/wiki/view_summary.js +++ b/redakcja/static/js/wiki/view_summary.js @@ -12,10 +12,10 @@ }); old_callback.call(this); - } + }; $.wiki.Perspective.call(this, options); - }; + } SummaryPerspective.prototype = new $.wiki.Perspective(); diff --git a/redakcja/static/js/wiki/wikiapi.js b/redakcja/static/js/wiki/wikiapi.js index d901e847..d9a183c4 100644 --- a/redakcja/static/js/wiki/wikiapi.js +++ b/redakcja/static/js/wiki/wikiapi.js @@ -1,73 +1,73 @@ (function($) { - $.wikiapi = {}; - var noop = function() { - }; - var noops = { - success: noop, - failure: noop - }; - /* - * Return absolute reverse path of given named view. (at least he have it - * hard-coded in one place) - * - * TODO: think of a way, not to hard-code it here ;) - * - */ - function reverse() { - var vname = arguments[0]; - var base_path = "/editor"; - - if (vname == "ajax_document_text") { - var path = "/text/" + arguments[1] + '/'; - - if (arguments[2] !== undefined) - path += arguments[2] + '/'; - - return base_path + path; - } + $.wikiapi = {}; + var noop = function() { + }; + var noops = { + success: noop, + failure: noop + }; + /* + * Return absolute reverse path of given named view. (at least he have it + * hard-coded in one place) + * + * TODO: think of a way, not to hard-code it here ;) + * + */ + function reverse() { + var vname = arguments[0]; + var base_path = "/editor"; + + if (vname == "ajax_document_text") { + var path = "/text/" + arguments[1] + '/'; + + if (arguments[2] !== undefined) + path += arguments[2] + '/'; + + return base_path + path; + } if (vname == "ajax_document_revert") { return base_path + "/revert/" + arguments[1] + '/'; } - if (vname == "ajax_document_history") { + if (vname == "ajax_document_history") { - return base_path + "/history/" + arguments[1] + '/'; - } + return base_path + "/history/" + arguments[1] + '/'; + } - if (vname == "ajax_document_gallery") { + if (vname == "ajax_document_gallery") { - return base_path + "/gallery/" + arguments[1] + '/'; - } + return base_path + "/gallery/" + arguments[1] + '/'; + } - if (vname == "ajax_document_diff") - return base_path + "/diff/" + arguments[1] + '/'; + if (vname == "ajax_document_diff") + return base_path + "/diff/" + arguments[1] + '/'; if (vname == "ajax_document_rev") return base_path + "/rev/" + arguments[1] + '/'; - if (vname == "ajax_document_pubmark") - return base_path + "/pubmark/" + arguments[1] + '/'; + if (vname == "ajax_document_pubmark") + return base_path + "/pubmark/" + arguments[1] + '/'; - if (vname == "ajax_cover_preview") - return "/cover/preview/"; + if (vname == "ajax_cover_preview") + return "/cover/preview/"; - console.log("Couldn't reverse match:", vname); - return "/404.html"; - }; + console.log("Couldn't reverse match:", vname); + return "/404.html"; + } - /* - * Document Abstraction - */ - function WikiDocument(element_id) { - var meta = $('#' + element_id); - this.id = meta.attr('data-chunk-id'); + /* + * Document Abstraction + */ + function WikiDocument(element_id) { + var meta = $('#' + element_id); + this.id = meta.attr('data-chunk-id'); - this.revision = $("*[data-key='revision']", meta).text(); - this.readonly = !!$("*[data-key='readonly']", meta).text(); + this.revision = $("*[data-key='revision']", meta).text(); + this.readonly = !!$("*[data-key='readonly']", meta).text(); - this.galleryLink = $("*[data-key='gallery']", meta).text(); + this.galleryLink = $("*[data-key='gallery']", meta).text(); this.galleryStart = parseInt($("*[data-key='gallery-start']", meta).text()); var diff = $("*[data-key='diff']", meta).text(); @@ -77,209 +77,193 @@ this.diff = diff; else if (diff.length == 1) { diff = parseInt(diff); - if (diff != NaN) + if (!isNaN(diff)) this.diff = [diff - 1, diff]; } } - this.galleryImages = []; - this.text = null; - this.has_local_changes = false; - this._lock = -1; - this._context_lock = -1; - this._lock_count = 0; - }; - - WikiDocument.prototype.triggerDocumentChanged = function() { - $(document).trigger('wlapi_document_changed', this); - }; - /* - * Fetch text of this document. - */ - WikiDocument.prototype.fetch = function(params) { - params = $.extend({}, noops, params); - var self = this; - $.ajax({ - method: "GET", - url: reverse("ajax_document_text", self.id), - data: {"revision": self.revision}, - dataType: 'json', - success: function(data) { - var changed = false; - - if (self.text === null || self.revision !== data.revision) { - self.text = data.text; - self.revision = data.revision; - self.gallery = data.gallery; - changed = true; - self.triggerDocumentChanged(); - }; - - self.has_local_changes = false; - params['success'](self, changed); - }, - error: function() { - params['failure'](self, "Nie udało się wczytać treści dokumentu."); - } - }); - }; - /* - * Fetch history of this document. - * - * from - First revision to fetch (default = 0) upto - Last revision to - * fetch (default = tip) - * - */ - WikiDocument.prototype.fetchHistory = function(params) { - /* this doesn't modify anything, so no locks */ - params = $.extend({}, noops, params); - var self = this; - $.ajax({ - method: "GET", - url: reverse("ajax_document_history", self.id), - dataType: 'json', - data: { - "from": params['from'], - "upto": params['upto'] - }, - success: function(data) { - params['success'](self, data); - }, - error: function() { - params['failure'](self, "Nie udało się wczytać historii dokumentu."); - } - }); - }; - WikiDocument.prototype.fetchDiff = function(params) { - /* this doesn't modify anything, so no locks */ - var self = this; - params = $.extend({ - 'from': self.revision, - 'to': self.revision - }, noops, params); - $.ajax({ - method: "GET", - url: reverse("ajax_document_diff", self.id), - dataType: 'html', - data: { - "from": params['from'], - "to": params['to'] - }, - success: function(data) { - params['success'](self, data); - }, - error: function() { - params['failure'](self, "Nie udało się wczytać porównania wersji."); - } - }); - }; - - WikiDocument.prototype.checkRevision = function(params) { - /* this doesn't modify anything, so no locks */ + this.galleryImages = []; + this.text = null; + this.has_local_changes = false; + this._lock = -1; + this._context_lock = -1; + this._lock_count = 0; + } + + WikiDocument.prototype.triggerDocumentChanged = function() { + $(document).trigger('wlapi_document_changed', this); + }; + /* + * Fetch text of this document. + */ + WikiDocument.prototype.fetch = function(params) { + params = $.extend({}, noops, params); var self = this; $.ajax({ method: "GET", - url: reverse("ajax_document_rev", self.id), - dataType: 'text', + url: reverse("ajax_document_text", self.id), + data: {"revision": self.revision}, + dataType: 'json', success: function(data) { - if (data == '') { - if (params.error) - params.error(); + var changed = false; + + if (self.text === null || self.revision !== data.revision) { + self.text = data.text; + self.revision = data.revision; + self.gallery = data.gallery; + changed = true; + self.triggerDocumentChanged(); } - else if (data != self.revision) - params.outdated(); + + self.has_local_changes = false; + params['success'](self, changed); + }, + error: function() { + params['failure'](self, "Nie udało się wczytać treści dokumentu."); + } + }); + }; + /* + * Fetch history of this document. + * + * from - First revision to fetch (default = 0) upto - Last revision to + * fetch (default = tip) + * + */ + WikiDocument.prototype.fetchHistory = function(params) { + /* this doesn't modify anything, so no locks */ + params = $.extend({}, noops, params); + var self = this; + $.ajax({ + method: "GET", + url: reverse("ajax_document_history", self.id), + dataType: 'json', + data: { + "from": params['from'], + "upto": params['upto'] + }, + success: function(data) { + params['success'](self, data); + }, + error: function() { + params['failure'](self, "Nie udało się wczytać historii dokumentu."); + } + }); + }; + WikiDocument.prototype.fetchDiff = function(params) { + /* this doesn't modify anything, so no locks */ + var self = this; + params = $.extend({ + 'from': self.revision, + 'to': self.revision + }, noops, params); + $.ajax({ + method: "GET", + url: reverse("ajax_document_diff", self.id), + dataType: 'html', + data: { + "from": params['from'], + "to": params['to'] + }, + success: function(data) { + params['success'](self, data); + }, + error: function() { + params['failure'](self, "Nie udało się wczytać porównania wersji."); } }); }; - /* - * Fetch gallery - */ - WikiDocument.prototype.refreshGallery = function(params) { - params = $.extend({}, noops, params); - var self = this; - $.ajax({ - method: "GET", - url: reverse("ajax_document_gallery", self.galleryLink), - dataType: 'json', - // data: {}, - success: function(data) { - self.galleryImages = data; - params['success'](self, data); - }, - error: function(xhr) { + /* + * Fetch gallery + */ + WikiDocument.prototype.refreshGallery = function(params) { + params = $.extend({}, noops, params); + var self = this; + $.ajax({ + method: "GET", + url: reverse("ajax_document_gallery", self.galleryLink), + dataType: 'json', + // data: {}, + success: function(data) { + self.galleryImages = data; + params['success'](self, data); + }, + error: function(xhr) { + var msg; switch (xhr.status) { case 403: - var msg = 'Galerie dostępne tylko dla zalogowanych użytkowników.'; + msg = 'Galerie dostępne tylko dla zalogowanych użytkowników.'; break; case 404: - var msg = "Nie znaleziono galerii o nazwie: '" + self.galleryLink + "'."; + msg = "Nie znaleziono galerii o nazwie: '" + self.galleryLink + "'."; + break; default: - var msg = "Nie udało się wczytać galerii o nazwie: '" + self.galleryLink + "'."; + msg = "Nie udało się wczytać galerii o nazwie: '" + self.galleryLink + "'."; } - self.galleryImages = []; - params['failure'](self, "

    " + msg + "

    "); - } - }); - }; - - /* - * Set document's text - */ - WikiDocument.prototype.setText = function(text) { - this.text = text; - this.has_local_changes = true; - }; - - /* - * Set document's gallery link - */ - WikiDocument.prototype.setGalleryLink = function(gallery) { - this.galleryLink = gallery; - this.has_local_changes = true; - }; - - /* - * Save text back to the server - */ - WikiDocument.prototype.save = function(params) { - params = $.extend({}, noops, params); - var self = this; - - if (!self.has_local_changes) { - console.log("Abort: no changes."); - return params['success'](self, false, "Nie ma zmian do zapisania."); - }; - - // Serialize form to dictionary - var data = {}; - $.each(params['form'].serializeArray(), function() { - data[this.name] = this.value; - }); - - data['textsave-text'] = self.text; - - $.ajax({ - url: reverse("ajax_document_text", self.id), - type: "POST", - dataType: "json", - data: data, - success: function(data) { - var changed = false; + self.galleryImages = []; + params['failure'](self, "

    " + msg + "

    "); + } + }); + }; + + /* + * Set document's text + */ + WikiDocument.prototype.setText = function(text) { + this.text = text; + this.has_local_changes = true; + }; + + /* + * Set document's gallery link + */ + WikiDocument.prototype.setGalleryLink = function(gallery) { + this.galleryLink = gallery; + this.has_local_changes = true; + }; + + /* + * Save text back to the server + */ + WikiDocument.prototype.save = function(params) { + params = $.extend({}, noops, params); + var self = this; + + if (!self.has_local_changes) { + console.log("Abort: no changes."); + return params['success'](self, false, "Nie ma zmian do zapisania."); + } + + // Serialize form to dictionary + var data = {}; + $.each(params['form'].serializeArray(), function() { + data[this.name] = this.value; + }); + + data['textsave-text'] = self.text; + + $.ajax({ + url: reverse("ajax_document_text", self.id), + type: "POST", + dataType: "json", + data: data, + success: function(data) { + var changed = false; $('#header').removeClass('saving'); - if (data.text) { - self.text = data.text; - self.revision = data.revision; - self.gallery = data.gallery; - changed = true; - self.triggerDocumentChanged(); - }; - - params['success'](self, changed, ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna")); - }, - error: function(xhr) { + if (data.text) { + self.text = data.text; + self.revision = data.revision; + self.gallery = data.gallery; + changed = true; + self.triggerDocumentChanged(); + } + + params['success'](self, changed, ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna")); + }, + error: function(xhr) { if ($('#header').hasClass('saving')) { $('#header').removeClass('saving'); $.blockUI({ @@ -294,18 +278,18 @@ params['failure'](self, { "__message": "

    Nie udało się zapisać - błąd serwera.

    " }); - }; + } } - } - }); + } + }); $('#save-hide').click(function(){ $('#header').addClass('saving'); $.unblockUI(); $.wiki.blocking.unblock(); }); - }; /* end of save() */ + }; /* end of save() */ WikiDocument.prototype.revertToVersion = function(params) { var self = this; @@ -346,51 +330,52 @@ }); }; - WikiDocument.prototype.pubmark = function(params) { - params = $.extend({}, noops, params); - var self = this; - var data = { - "pubmark-id": self.id, - }; - - /* unpack form */ - $.each(params.form.serializeArray(), function() { - data[this.name] = this.value; - }); - - $.ajax({ - url: reverse("ajax_document_pubmark", self.id), - type: "POST", - dataType: "json", - data: data, - success: function(data) { - params.success(self, data.message); - }, - error: function(xhr) { - if (xhr.status == 403 || xhr.status == 401) { - params.failure(self, { - "__all__": ["Nie masz uprawnień lub nie jesteś zalogowany."] - }); - } - else { - try { - params.failure(self, $.parseJSON(xhr.responseText)); - } - catch (e) { - params.failure(self, { - "__all__": ["Nie udało się - błąd serwera."] - }); - }; - }; - } - }); - }; - - WikiDocument.prototype.refreshCover = function(params) { + WikiDocument.prototype.pubmark = function(params) { + params = $.extend({}, noops, params); var self = this; - var data = { - xml: self.text // TODO: send just DC - }; + var data = { + "pubmark-id": self.id + }; + + /* unpack form */ + $.each(params.form.serializeArray(), function() { + data[this.name] = this.value; + }); + + $.ajax({ + url: reverse("ajax_document_pubmark", self.id), + type: "POST", + dataType: "json", + data: data, + success: function(data) { + params.success(self, data.message); + }, + error: function(xhr) { + if (xhr.status == 403 || xhr.status == 401) { + params.failure(self, { + "__all__": ["Nie masz uprawnień lub nie jesteś zalogowany."] + }); + } + else { + try { + params.failure(self, $.parseJSON(xhr.responseText)); + } + catch (e) { + params.failure(self, { + "__all__": ["Nie udało się - błąd serwera."] + }); + } + } + } + }); + }; + + /* unused except view_summary.js which is apparently unused */ + WikiDocument.prototype.refreshCover = function(params) { + var self = this; + var data = { + xml: self.text // TODO: send just DC + }; $.ajax({ url: reverse("ajax_cover_preview"), type: "POST", @@ -402,7 +387,7 @@ // params.failure("Nie udało się odświeżyć okładki - błąd serwera."); } }); - }; + }; WikiDocument.prototype.getLength = function(params) { @@ -419,8 +404,8 @@ var text = $(doc).text(); text = $.trim(text.replace(/\s{2,}/g, ' ')); return text.length; - } + }; - $.wikiapi.WikiDocument = WikiDocument; + $.wikiapi.WikiDocument = WikiDocument; })(jQuery); diff --git a/redakcja/templates/registration/head_login.html b/redakcja/templates/registration/head_login.html index 5223da07..f743f409 100644 --- a/redakcja/templates/registration/head_login.html +++ b/redakcja/templates/registration/head_login.html @@ -59,7 +59,6 @@ {% endif %}
  • {% trans "Logout" %}
  • - @@ -74,8 +73,8 @@ > {% trans "Log in / Register" %} -