From: Radek Czajka Date: Thu, 27 Mar 2014 14:52:59 +0000 (+0100) Subject: Use fnpdjango and AttrCASBackend. X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/3e569a771ed8cb9ab7156425ff870dc9d33ef023?hp=342d7985a8765b226a416585a3cc01cecff9a394 Use fnpdjango and AttrCASBackend. Remove some stale lib/ stuff. --- diff --git a/apps/catalogue/management/commands/assign_from_redmine.py b/apps/catalogue/management/commands/assign_from_redmine.py index 9f7b12d4..03c0637d 100644 --- a/apps/catalogue/management/commands/assign_from_redmine.py +++ b/apps/catalogue/management/commands/assign_from_redmine.py @@ -12,7 +12,7 @@ from django.core.management.base import BaseCommand from django.core.management.color import color_style from django.db import transaction -from slughifi import slughifi +from fnpdjango.utils.text.slughifi import slughifi from catalogue.models import Chunk diff --git a/apps/catalogue/management/commands/merge_books.py b/apps/catalogue/management/commands/merge_books.py index aec113ed..62f8381e 100644 --- a/apps/catalogue/management/commands/merge_books.py +++ b/apps/catalogue/management/commands/merge_books.py @@ -8,7 +8,7 @@ from django.core.management.base import BaseCommand from django.core.management.color import color_style from django.db import transaction -from slughifi import slughifi +from fnpdjango.utils.text.slughifi import slughifi from catalogue.models import Book diff --git a/apps/catalogue/migrations/0003_from_hg.py b/apps/catalogue/migrations/0003_from_hg.py index d8360676..c0b4dad8 100644 --- a/apps/catalogue/migrations/0003_from_hg.py +++ b/apps/catalogue/migrations/0003_from_hg.py @@ -11,7 +11,7 @@ from south.db import db from south.v2 import DataMigration from django.conf import settings -from slughifi import slughifi +from fnpdjango.utils.text.slughifi import slughifi META_REGEX = re.compile(r'\s*', re.DOTALL | re.MULTILINE) STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE) diff --git a/apps/catalogue/models/book.py b/apps/catalogue/models/book.py index 7c2bccc4..5902ae9e 100755 --- a/apps/catalogue/models/book.py +++ b/apps/catalogue/models/book.py @@ -8,7 +8,7 @@ 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 +from fnpdjango.utils.text.slughifi import slughifi import apiclient diff --git a/apps/catalogue/templatetags/common_tags.py b/apps/catalogue/templatetags/common_tags.py index 6baf4e51..7f5d0e9f 100755 --- a/apps/catalogue/templatetags/common_tags.py +++ b/apps/catalogue/templatetags/common_tags.py @@ -1,11 +1,6 @@ from django import template register = template.Library() - -@register.filter -def build_absolute_uri(uri, request): - return request.build_absolute_uri(uri) - @register.filter def username(user): return ("%s %s" % (user.first_name, user.last_name)).lstrip() or user.username diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index 237e4a70..ef8d83a5 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -142,7 +142,7 @@ def upload(request): if request.method == "POST": form = forms.DocumentsUploadForm(request.POST, request.FILES) if form.is_valid(): - import slughifi + from fnpdjango.utils.text.slughifi import slughifi if request.user.is_authenticated(): creator = request.user diff --git a/apps/cover/templates/cover/image_detail.html b/apps/cover/templates/cover/image_detail.html index ee6a762f..d09a6580 100755 --- a/apps/cover/templates/cover/image_detail.html +++ b/apps/cover/templates/cover/image_detail.html @@ -1,7 +1,7 @@ {% extends "catalogue/base.html" %} {% load i18n %} {% load thumbnail %} -{% load build_absolute_uri from common_tags %} +{% load build_absolute_uri from fnp_common %} {% block content %}

{% trans "Cover image" %}

diff --git a/lib/slughifi.py b/lib/slughifi.py deleted file mode 100644 index fe5c9e3e..00000000 --- a/lib/slughifi.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- -import re -from types import UnicodeType - -from django.template.defaultfilters import slugify - -# default unicode character mapping ( you may not see some chars, leave as is ) -char_map = {u'À': 'A', u'Á': 'A', u'Â': 'A', u'Ã': 'A', u'Ä': 'Ae', u'Å': 'A', u'Æ': 'A', u'Ā': 'A', u'Ą': 'A', u'Ă': 'A', u'Ç': 'C', u'Ć': 'C', u'Č': 'C', u'Ĉ': 'C', u'Ċ': 'C', u'Ď': 'D', u'Đ': 'D', u'È': 'E', u'É': 'E', u'Ê': 'E', u'Ë': 'E', u'Ē': 'E', u'Ę': 'E', u'Ě': 'E', u'Ĕ': 'E', u'Ė': 'E', u'Ĝ': 'G', u'Ğ': 'G', u'Ġ': 'G', u'Ģ': 'G', u'Ĥ': 'H', u'Ħ': 'H', u'Ì': 'I', u'Í': 'I', u'Î': 'I', u'Ï': 'I', u'Ī': 'I', u'Ĩ': 'I', u'Ĭ': 'I', u'Į': 'I', u'İ': 'I', u'IJ': 'IJ', u'Ĵ': 'J', u'Ķ': 'K', u'Ľ': 'K', u'Ĺ': 'K', u'Ļ': 'K', u'Ŀ': 'K', u'Ł': 'L', u'Ñ': 'N', u'Ń': 'N', u'Ň': 'N', u'Ņ': 'N', u'Ŋ': 'N', u'Ò': 'O', u'Ó': 'O', u'Ô': 'O', u'Õ': 'O', u'Ö': 'Oe', u'Ø': 'O', u'Ō': 'O', u'Ő': 'O', u'Ŏ': 'O', u'Œ': 'OE', u'Ŕ': 'R', u'Ř': 'R', u'Ŗ': 'R', u'Ś': 'S', u'Ş': 'S', u'Ŝ': 'S', u'Ș': 'S', u'Š': 'S', u'Ť': 'T', u'Ţ': 'T', u'Ŧ': 'T', u'Ț': 'T', u'Ù': 'U', u'Ú': 'U', u'Û': 'U', u'Ü': 'Ue', u'Ū': 'U', u'Ů': 'U', u'Ű': 'U', u'Ŭ': 'U', u'Ũ': 'U', u'Ų': 'U', u'Ŵ': 'W', u'Ŷ': 'Y', u'Ÿ': 'Y', u'Ý': 'Y', u'Ź': 'Z', u'Ż': 'Z', u'Ž': 'Z', u'à': 'a', u'á': 'a', u'â': 'a', u'ã': 'a', u'ä': 'ae', u'ā': 'a', u'ą': 'a', u'ă': 'a', u'å': 'a', u'æ': 'ae', u'ç': 'c', u'ć': 'c', u'č': 'c', u'ĉ': 'c', u'ċ': 'c', u'ď': 'd', u'đ': 'd', u'è': 'e', u'é': 'e', u'ê': 'e', u'ë': 'e', u'ē': 'e', u'ę': 'e', u'ě': 'e', u'ĕ': 'e', u'ė': 'e', u'ƒ': 'f', u'ĝ': 'g', u'ğ': 'g', u'ġ': 'g', u'ģ': 'g', u'ĥ': 'h', u'ħ': 'h', u'ì': 'i', u'í': 'i', u'î': 'i', u'ï': 'i', u'ī': 'i', u'ĩ': 'i', u'ĭ': 'i', u'į': 'i', u'ı': 'i', u'ij': 'ij', u'ĵ': 'j', u'ķ': 'k', u'ĸ': 'k', u'ł': 'l', u'ľ': 'l', u'ĺ': 'l', u'ļ': 'l', u'ŀ': 'l', u'ñ': 'n', u'ń': 'n', u'ň': 'n', u'ņ': 'n', u'ʼn': 'n', u'ŋ': 'n', u'ò': 'o', u'ó': 'o', u'ô': 'o', u'õ': 'o', u'ö': 'oe', u'ø': 'o', u'ō': 'o', u'ő': 'o', u'ŏ': 'o', u'œ': 'oe', u'ŕ': 'r', u'ř': 'r', u'ŗ': 'r', u'ś': 's', u'š': 's', u'ť': 't', u'ù': 'u', u'ú': 'u', u'û': 'u', u'ü': 'ue', u'ū': 'u', u'ů': 'u', u'ű': 'u', u'ŭ': 'u', u'ũ': 'u', u'ų': 'u', u'ŵ': 'w', u'ÿ': 'y', u'ý': 'y', u'ŷ': 'y', u'ż': 'z', u'ź': 'z', u'ž': 'z', u'ß': 'ss', u'ſ': 'ss', u'Α': 'A', u'Ά': 'A', u'Ἀ': 'A', u'Ἁ': 'A', u'Ἂ': 'A', u'Ἃ': 'A', u'Ἄ': 'A', u'Ἅ': 'A', u'Ἆ': 'A', u'Ἇ': 'A', u'ᾈ': 'A', u'ᾉ': 'A', u'ᾊ': 'A', u'ᾋ': 'A', u'ᾌ': 'A', u'ᾍ': 'A', u'ᾎ': 'A', u'ᾏ': 'A', u'Ᾰ': 'A', u'Ᾱ': 'A', u'Ὰ': 'A', u'Ά': 'A', u'ᾼ': 'A', u'Β': 'B', u'Γ': 'G', u'Δ': 'D', u'Ε': 'E', u'Έ': 'E', u'Ἐ': 'E', u'Ἑ': 'E', u'Ἒ': 'E', u'Ἓ': 'E', u'Ἔ': 'E', u'Ἕ': 'E', u'Έ': 'E', u'Ὲ': 'E', u'Ζ': 'Z', u'Η': 'I', u'Ή': 'I', u'Ἠ': 'I', u'Ἡ': 'I', u'Ἢ': 'I', u'Ἣ': 'I', u'Ἤ': 'I', u'Ἥ': 'I', u'Ἦ': 'I', u'Ἧ': 'I', u'ᾘ': 'I', u'ᾙ': 'I', u'ᾚ': 'I', u'ᾛ': 'I', u'ᾜ': 'I', u'ᾝ': 'I', u'ᾞ': 'I', u'ᾟ': 'I', u'Ὴ': 'I', u'Ή': 'I', u'ῌ': 'I', u'Θ': 'TH', u'Ι': 'I', u'Ί': 'I', u'Ϊ': 'I', u'Ἰ': 'I', u'Ἱ': 'I', u'Ἲ': 'I', u'Ἳ': 'I', u'Ἴ': 'I', u'Ἵ': 'I', u'Ἶ': 'I', u'Ἷ': 'I', u'Ῐ': 'I', u'Ῑ': 'I', u'Ὶ': 'I', u'Ί': 'I', u'Κ': 'K', u'Λ': 'L', u'Μ': 'M', u'Ν': 'N', u'Ξ': 'KS', u'Ο': 'O', u'Ό': 'O', u'Ὀ': 'O', u'Ὁ': 'O', u'Ὂ': 'O', u'Ὃ': 'O', u'Ὄ': 'O', u'Ὅ': 'O', u'Ὸ': 'O', u'Ό': 'O', u'Π': 'P', u'Ρ': 'R', u'Ῥ': 'R', u'Σ': 'S', u'Τ': 'T', u'Υ': 'Y', u'Ύ': 'Y', u'Ϋ': 'Y', u'Ὑ': 'Y', u'Ὓ': 'Y', u'Ὕ': 'Y', u'Ὗ': 'Y', u'Ῠ': 'Y', u'Ῡ': 'Y', u'Ὺ': 'Y', u'Ύ': 'Y', u'Φ': 'F', u'Χ': 'X', u'Ψ': 'PS', u'Ω': 'O', u'Ώ': 'O', u'Ὠ': 'O', u'Ὡ': 'O', u'Ὢ': 'O', u'Ὣ': 'O', u'Ὤ': 'O', u'Ὥ': 'O', u'Ὦ': 'O', u'Ὧ': 'O', u'ᾨ': 'O', u'ᾩ': 'O', u'ᾪ': 'O', u'ᾫ': 'O', u'ᾬ': 'O', u'ᾭ': 'O', u'ᾮ': 'O', u'ᾯ': 'O', u'Ὼ': 'O', u'Ώ': 'O', u'ῼ': 'O', u'α': 'a', u'ά': 'a', u'ἀ': 'a', u'ἁ': 'a', u'ἂ': 'a', u'ἃ': 'a', u'ἄ': 'a', u'ἅ': 'a', u'ἆ': 'a', u'ἇ': 'a', u'ᾀ': 'a', u'ᾁ': 'a', u'ᾂ': 'a', u'ᾃ': 'a', u'ᾄ': 'a', u'ᾅ': 'a', u'ᾆ': 'a', u'ᾇ': 'a', u'ὰ': 'a', u'ά': 'a', u'ᾰ': 'a', u'ᾱ': 'a', u'ᾲ': 'a', u'ᾳ': 'a', u'ᾴ': 'a', u'ᾶ': 'a', u'ᾷ': 'a', u'β': 'b', u'γ': 'g', u'δ': 'd', u'ε': 'e', u'έ': 'e', u'ἐ': 'e', u'ἑ': 'e', u'ἒ': 'e', u'ἓ': 'e', u'ἔ': 'e', u'ἕ': 'e', u'ὲ': 'e', u'έ': 'e', u'ζ': 'z', u'η': 'i', u'ή': 'i', u'ἠ': 'i', u'ἡ': 'i', u'ἢ': 'i', u'ἣ': 'i', u'ἤ': 'i', u'ἥ': 'i', u'ἦ': 'i', u'ἧ': 'i', u'ᾐ': 'i', u'ᾑ': 'i', u'ᾒ': 'i', u'ᾓ': 'i', u'ᾔ': 'i', u'ᾕ': 'i', u'ᾖ': 'i', u'ᾗ': 'i', u'ὴ': 'i', u'ή': 'i', u'ῂ': 'i', u'ῃ': 'i', u'ῄ': 'i', u'ῆ': 'i', u'ῇ': 'i', u'θ': 'th', u'ι': 'i', u'ί': 'i', u'ϊ': 'i', u'ΐ': 'i', u'ἰ': 'i', u'ἱ': 'i', u'ἲ': 'i', u'ἳ': 'i', u'ἴ': 'i', u'ἵ': 'i', u'ἶ': 'i', u'ἷ': 'i', u'ὶ': 'i', u'ί': 'i', u'ῐ': 'i', u'ῑ': 'i', u'ῒ': 'i', u'ΐ': 'i', u'ῖ': 'i', u'ῗ': 'i', u'κ': 'k', u'λ': 'l', u'μ': 'm', u'ν': 'n', u'ξ': 'ks', u'ο': 'o', u'ό': 'o', u'ὀ': 'o', u'ὁ': 'o', u'ὂ': 'o', u'ὃ': 'o', u'ὄ': 'o', u'ὅ': 'o', u'ὸ': 'o', u'ό': 'o', u'π': 'p', u'ρ': 'r', u'ῤ': 'r', u'ῥ': 'r', u'σ': 's', u'ς': 's', u'τ': 't', u'υ': 'y', u'ύ': 'y', u'ϋ': 'y', u'ΰ': 'y', u'ὐ': 'y', u'ὑ': 'y', u'ὒ': 'y', u'ὓ': 'y', u'ὔ': 'y', u'ὕ': 'y', u'ὖ': 'y', u'ὗ': 'y', u'ὺ': 'y', u'ύ': 'y', u'ῠ': 'y', u'ῡ': 'y', u'ῢ': 'y', u'ΰ': 'y', u'ῦ': 'y', u'ῧ': 'y', u'φ': 'f', u'χ': 'x', u'ψ': 'ps', u'ω': 'o', u'ώ': 'o', u'ὠ': 'o', u'ὡ': 'o', u'ὢ': 'o', u'ὣ': 'o', u'ὤ': 'o', u'ὥ': 'o', u'ὦ': 'o', u'ὧ': 'o', u'ᾠ': 'o', u'ᾡ': 'o', u'ᾢ': 'o', u'ᾣ': 'o', u'ᾤ': 'o', u'ᾥ': 'o', u'ᾦ': 'o', u'ᾧ': 'o', u'ὼ': 'o', u'ώ': 'o', u'ῲ': 'o', u'ῳ': 'o', u'ῴ': 'o', u'ῶ': 'o', u'ῷ': 'o', u'¨': '', u'΅': '', u'᾿': '', u'῾': '', u'῍': '', u'῝': '', u'῎': '', u'῞': '', u'῏': '', u'῟': '', u'῀': '', u'῁': '', u'΄': '', u'΅': '', u'`': '', u'῭': '', u'ͺ': '', u'᾽': '', u'А': 'A', u'Б': 'B', u'В': 'V', u'Г': 'G', u'Д': 'D', u'Е': 'E', u'Ё': 'E', u'Ж': 'ZH', u'З': 'Z', u'И': 'I', u'Й': 'I', u'К': 'K', u'Л': 'L', u'М': 'M', u'Н': 'N', u'О': 'O', u'П': 'P', u'Р': 'R', u'С': 'S', u'Т': 'T', u'У': 'U', u'Ф': 'F', u'Х': 'KH', u'Ц': 'TS', u'Ч': 'CH', u'Ш': 'SH', u'Щ': 'SHCH', u'Ы': 'Y', u'Э': 'E', u'Ю': 'YU', u'Я': 'YA', u'а': 'A', u'б': 'B', u'в': 'V', u'г': 'G', u'д': 'D', u'е': 'E', u'ё': 'E', u'ж': 'ZH', u'з': 'Z', u'и': 'I', u'й': 'I', u'к': 'K', u'л': 'L', u'м': 'M', u'н': 'N', u'о': 'O', u'п': 'P', u'р': 'R', u'с': 'S', u'т': 'T', u'у': 'U', u'ф': 'F', u'х': 'KH', u'ц': 'TS', u'ч': 'CH', u'ш': 'SH', u'щ': 'SHCH', u'ы': 'Y', u'э': 'E', u'ю': 'YU', u'я': 'YA', u'Ъ': '', u'ъ': '', u'Ь': '', u'ь': '', u'ð': 'd', u'Ð': 'D', u'þ': 'th', u'Þ': 'TH', - u'ა': 'a', u'ბ': 'b', u'გ': 'g', u'დ': 'd', u'ე': 'e', u'ვ': 'v', u'ზ': 'z', u'თ': 't', u'ი': 'i', u'კ': 'k', u'ლ': 'l', u'მ': 'm', u'ნ': 'n', u'ო': 'o', u'პ': 'p', u'ჟ': 'zh', u'რ': 'r', u'ს': 's', u'ტ': 't', u'უ': 'u', u'ფ': 'p', u'ქ': 'k', u'ღ': 'gh', u'ყ': 'q', u'შ': 'sh', u'ჩ': 'ch', u'ც': 'ts', u'ძ': 'dz', u'წ': 'ts', u'ჭ': 'ch', u'ხ': 'kh', u'ჯ': 'j', u'ჰ': 'h' } - -def replace_char(m): - char = m.group() - if char_map.has_key(char): - return char_map[char] - else: - return char - -def slughifi(value, do_slugify=True, overwrite_char_map={}): - """ - High Fidelity slugify - slughifi.py, v 0.1 - - Examples : - - >>> text = 'C\'est déjà l\'été.' - - >>> slughifi(text) - 'cest-deja-lete' - - >>> slughifi(text, overwrite_char_map={u'\'': '-',}) - 'c-est-deja-l-ete' - - >>> slughifi(text, do_slugify=False) - "C'est deja l'ete." - - # Normal slugify removes accented characters - >>> slugify(text) - 'cest-dj-lt' - - """ - - # unicodification - if type(value) != UnicodeType: - value = unicode(value, 'utf-8', 'ignore') - - # overwrite chararcter mapping - char_map.update(overwrite_char_map) - - # try to replace chars - value = re.sub('[^a-zA-Z0-9\\s\\-]{1}', replace_char, value) - - # apply django default slugify - if do_slugify: - value = slugify(value) - - return value.encode('ascii', 'ignore') - diff --git a/lib/vstorage/__init__.py b/lib/vstorage/__init__.py deleted file mode 100644 index 2708ed79..00000000 --- a/lib/vstorage/__init__.py +++ /dev/null @@ -1,445 +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 -import tempfile -import datetime -import mimetypes -import urllib -import functools - -import logging -logger = logging.getLogger('fnp.hazlenut.vstorage') - -# Note: we have to set these before importing Mercurial -os.environ['HGENCODING'] = 'utf-8' -os.environ['HGMERGE'] = "internal:merge" - -import mercurial.hg -import mercurial.revlog -import mercurial.util - -from vstorage.hgui import SilentUI - - -def urlquote(url, safe='/'): - """Quotes URL - - >>> urlquote(u'Za\u017c\xf3\u0142\u0107 g\u0119\u015bl\u0105 ja\u017a\u0144') - 'Za%C5%BC%C3%B3%C5%82%C4%87%20g%C4%99%C5%9Bl%C4%85%20ja%C5%BA%C5%84' - """ - return urllib.quote(url.encode('utf-8', 'ignore'), safe) - - -def urlunquote(url): - """Unqotes URL - - # >>> urlunquote('Za%C5%BC%C3%B3%C5%82%C4%87_g%C4%99%C5%9Bl%C4%85_ja%C5%BA%C5%84') - # u'Za\u017c\xf3\u0142\u0107_g\u0119\u015bl\u0105 ja\u017a\u0144' - """ - return unicode(urllib.unquote(url), 'utf-8', 'ignore') - - -def find_repo_path(path): - """Go up the directory tree looking for a Mercurial repository (a directory containing a .hg subdirectory).""" - while not os.path.isdir(os.path.join(path, ".hg")): - old_path, path = path, os.path.dirname(path) - if path == old_path: - return None - return path - - -def with_working_copy_locked(func): - """A decorator for locking the repository when calling a method.""" - - @functools.wraps(func) - def wrapped(self, *args, **kwargs): - """Wrap the original function in locks.""" - wlock = self.repo.wlock() - try: - return func(self, *args, **kwargs) - finally: - wlock.release() - return wrapped - - -def with_storage_locked(func): - """A decorator for locking the repository when calling a method.""" - - @functools.wraps(func) - def wrapped(self, *args, **kwargs): - """Wrap the original function in locks.""" - lock = self.repo.lock() - try: - return func(self, *args, **kwargs) - finally: - lock.release() - return wrapped - - -def guess_mime(file_name): - """ - Guess file's mime type based on extension. - Default of text/x-wiki for files without an extension. - - >>> guess_mime('something.txt') - 'text/plain' - >>> guess_mime('SomePage') - 'text/x-wiki' - >>> guess_mime(u'ąęśUnicodePage') - 'text/x-wiki' - >>> guess_mime('image.png') - 'image/png' - >>> guess_mime('style.css') - 'text/css' - >>> guess_mime('archive.tar.gz') - 'archive/gzip' - """ - - mime, encoding = mimetypes.guess_type(file_name, strict=False) - if encoding: - mime = 'archive/%s' % encoding - if mime is None: - mime = 'text/x-wiki' - return mime - - -class DocumentNotFound(Exception): - pass - - -class VersionedStorage(object): - """ - Provides means of storing text pages and keeping track of their - change history, using Mercurial repository as the storage method. - """ - - def __init__(self, path, charset=None): - """ - Takes the path to the directory where the pages are to be kept. - If the directory doen't exist, it will be created. If it's inside - a Mercurial repository, that repository will be used, otherwise - a new repository will be created in it. - """ - - self.charset = charset or 'utf-8' - self.path = path - if not os.path.exists(self.path): - os.makedirs(self.path) - self.repo_path = find_repo_path(self.path) - - self.ui = SilentUI() - - if self.repo_path is None: - self.repo_path = self.path - create = True - else: - create = False - - self.repo_prefix = self.path[len(self.repo_path):].strip('/') - self.repo = mercurial.hg.repository(self.ui, self.repo_path, - create=create) - - def reopen(self): - """Close and reopen the repo, to make sure we are up to date.""" - self.repo = mercurial.hg.repository(self.ui, self.repo_path) - - def _file_path(self, title, type='.xml'): - """ Return plain version if exists in repo, add extension otherwise. """ - path = os.path.join(self.path, urlquote(title, safe='')) - if type and self._title_to_file(title, '') not in self.repo['tip']: - path += type - return path - - def _title_to_file(self, title, type=".xml"): - """ Return plain version if exists in repo, add extension otherwise. """ - path = os.path.join(self.repo_prefix, urlquote(title, safe='')) - if type and path not in self.repo['tip']: - path += type - return path - - def _file_to_title(self, filename): - assert filename.startswith(self.repo_prefix) - name = filename[len(self.repo_prefix):].strip('/').rsplit('.', 1)[0] - return urlunquote(name) - - def __contains__(self, title): - return self._title_to_file(title) in self.repo['tip'] - - def __iter__(self): - return self.all_pages() - - def merge_changes(self, changectx, repo_file, text, user, parent): - """Commits and merges conflicting changes in the repository.""" - tip_node = changectx.node() - filectx = changectx[repo_file].filectx(parent) - parent_node = filectx.changectx().node() - - self.repo.dirstate.setparents(parent_node) - node = self._commit([repo_file], text, user) - - partial = lambda filename: repo_file == filename - - # If p1 is equal to p2, there is no work to do. Even the dirstate is correct. - p1, p2 = self.repo[None].parents()[0], self.repo[tip_node] - if p1 == p2: - return text - - try: - mercurial.merge.update(self.repo, tip_node, True, False, partial) - msg = 'merge of edit conflict' - except mercurial.util.Abort: - msg = 'failed merge of edit conflict' - - self.repo.dirstate.setparents(tip_node, node) - # Mercurial 1.1 and later need updating the merge state - try: - mercurial.merge.mergestate(self.repo).mark(repo_file, "r") - except (AttributeError, KeyError): - pass - return msg - - @with_working_copy_locked - @with_storage_locked - def save_file(self, title, file_name, **kwargs): - """Save an existing file as specified page.""" - - author = kwargs.get('author', u'anonymous').encode('utf-8') - comment = kwargs.get('comment', u'Empty comment.').encode('utf-8') - parent = kwargs.get('parent', None) - - repo_file = self._title_to_file(title) - file_path = self._file_path(title) - mercurial.util.rename(file_name, file_path) - changectx = self._changectx() - - try: - filectx_tip = changectx[repo_file] - current_page_rev = filectx_tip.filerev() - except mercurial.revlog.LookupError: - self.repo[None].add([repo_file]) - current_page_rev = -1 - - if parent is not None and current_page_rev != parent: - msg = self.merge_changes(changectx, repo_file, comment, author, parent) - author = '' - comment = msg.encode('utf-8') - - logger.debug("Commiting %r", repo_file) - - self._commit([repo_file], comment, author) - - def save_data(self, title, data, **kwargs): - """Save data as specified page.""" - try: - temp_path = tempfile.mkdtemp(dir=self.path) - file_path = os.path.join(temp_path, 'saved') - f = open(file_path, "wb") - f.write(data) - f.close() - - return self.save_file(title=title, file_name=file_path, **kwargs) - finally: - try: - os.unlink(file_path) - except OSError: - pass - try: - os.rmdir(temp_path) - except OSError: - pass - - def save_text(self, **kwargs): - """Save text as specified page, encoded to charset.""" - text = kwargs.pop('text') - return self.save_data(data=text.encode(self.charset), **kwargs) - - def _commit(self, files, comment, user): - match = mercurial.match.exact(self.repo_path, '', list(files)) - return self.repo.commit(match=match, text=comment, user=user, force=True) - - @with_working_copy_locked - @with_storage_locked - def delete_page(self, title, author=u'', comment=u''): - user = author.encode('utf-8') or 'anon' - text = comment.encode('utf-8') or 'deleted' - repo_file = self._title_to_file(title) - file_path = self._file_path(title) - try: - os.unlink(file_path) - except OSError: - pass - self.repo[None].remove([repo_file]) - self._commit([repo_file], text, user) - - def page_text(self, title, revision=None): - """Read unicode text of a page.""" - ctx = self._find_filectx(title, revision) - - if ctx is None: - raise DocumentNotFound(title) - - return ctx.data().decode(self.charset, 'replace'), ctx.filerev() - - def page_text_by_tag(self, title, tag): - """Read unicode text of a taged page.""" - fname = self._title_to_file(title) - tag = u"{fname}#{tag}".format(**locals()).encode('utf-8') - - try: - ctx = self.repo[tag][fname] - return ctx.data().decode(self.charset, 'replace'), ctx.filerev() - except IndexError: - raise DocumentNotFound(fname) - - @with_working_copy_locked - def page_file_meta(self, title): - """Get page's inode number, size and last modification time.""" - try: - (_st_mode, st_ino, _st_dev, _st_nlink, _st_uid, _st_gid, st_size, - _st_atime, st_mtime, _st_ctime) = os.stat(self._file_path(title)) - except OSError: - return 0, 0, 0 - return st_ino, st_size, st_mtime - - @with_working_copy_locked - def page_meta(self, title, revision=None): - """Get page's revision, date, last editor and his edit comment.""" - fctx = self._find_filectx(title, revision) - - if fctx is None: - raise DocumentNotFound(title) - - return { - "revision": fctx.filerev(), - "date": datetime.datetime.fromtimestamp(fctx.date()[0]), - "author": fctx.user().decode("utf-8", 'replace'), - "comment": fctx.description().decode("utf-8", 'replace'), - } - - def repo_revision(self): - return self.repo['tip'].rev() - - def _changectx(self): - return self.repo['tip'] - - def page_mime(self, title): - """ - Guess page's mime type based on corresponding file name. - Default ot text/x-wiki for files without an extension. - """ - return guess_mime(self._file_path(title)) - - def _find_filectx(self, title, rev=None): - """ - Find the revision of the file in repo. - Only look for files still existing in repo's tip. - """ - tip = self._changectx() - file = self._title_to_file(title) - logging.info('Looking for %s', file) - if file in tip: - fctx = tip[file] - else: - file = self._title_to_file(title, type='') - logging.info('.xml not found, trying plain') - if file in tip: - fctx = tip[file] - else: - raise DocumentNotFound(title) - - if rev is not None: - fctx = fctx.filectx(rev) - fctx.filerev() - return fctx - - def page_history(self, title): - """Iterate over the page's history.""" - - filectx_tip = self._find_filectx(title) - - maxrev = filectx_tip.filerev() - minrev = 0 - for rev in range(maxrev, minrev - 1, -1): - filectx = filectx_tip.filectx(rev) - date = datetime.datetime.fromtimestamp(filectx.date()[0]) - author = filectx.user().decode('utf-8', 'replace') - comment = filectx.description().decode("utf-8", 'replace') - tags = [t.rsplit('#', 1)[-1] for t in filectx.changectx().tags() if '#' in t] - - yield { - "version": rev, - "date": date, - "author": author, - "description": comment, - "tag": tags, - } - - @with_working_copy_locked - def add_page_tag(self, title, rev, tag, user, doctag=True): - ctitle = self._title_to_file(title) - - if doctag: - tag = u"{ctitle}#{tag}".format(**locals()).encode('utf-8') - - message = u"Assigned tag {tag!r} to version {rev!r} of {ctitle!r}".format(**locals()).encode('utf-8') - - fctx = self._find_filectx(title, rev) - self.repo.tag( - names=tag, node=fctx.node(), local=False, - user=user, message=message, date=None, - ) - - def history(self): - """Iterate over the history of entire wiki.""" - - changectx = self._changectx() - maxrev = changectx.rev() - minrev = 0 - for wiki_rev in range(maxrev, minrev - 1, -1): - change = self.repo.changectx(wiki_rev) - date = datetime.datetime.fromtimestamp(change.date()[0]) - author = change.user().decode('utf-8', 'replace') - comment = change.description().decode("utf-8", 'replace') - for repo_file in change.files(): - if repo_file.startswith(self.repo_prefix): - title = self._file_to_title(repo_file) - try: - rev = change[repo_file].filerev() - except mercurial.revlog.LookupError: - rev = -1 - yield title, rev, date, author, comment - - def all_pages(self, type=''): - tip = self.repo['tip'] - """Iterate over the titles of all pages in the wiki.""" - return [self._file_to_title(filename) for filename in tip - if not filename.startswith('.') - and filename.endswith(type) ] - - def changed_since(self, rev): - """Return all pages that changed since specified repository revision.""" - - try: - last = self.repo.lookup(int(rev)) - except IndexError: - for page in self.all_pages(): - yield page - return - current = self.repo.lookup('tip') - status = self.repo.status(current, last) - modified, added, removed, deleted, unknown, ignored, clean = status - for filename in modified + added + removed + deleted: - if filename.startswith(self.repo_prefix): - yield self._file_to_title(filename) - - def revert(self, pageid, rev, **commit_args): - """ Make the given version of page the current version (reverting changes). """ - - # Find the old version - fctx = self._find_filectx(pageid, rev) - - # Restore the contents - self.save_data(pageid, fctx.data(), **commit_args) diff --git a/lib/vstorage/hgui.py b/lib/vstorage/hgui.py deleted file mode 100644 index 36c6e230..00000000 --- a/lib/vstorage/hgui.py +++ /dev/null @@ -1,57 +0,0 @@ -""" - Mercurial ui module replacement. -""" - -import mercurial.ui -import logging - -class SilentUI(mercurial.ui.ui): - - def __init__(self, *args, **kwargs): - super(SilentUI, self).__init__(*args, **kwargs) - - # make sure this doesn't collide with anything in Mercurial - self.__logger = logging.getLogger('mercurial') - - def _is_trusted(self, fd, filename): - """ Checks if config file is trusted - on server side, this isn't very useful. """ - return True - - def write(self, *args): - if self._buffers: - self._buffers[-1].extend([str(a) for a in args]) - else: - self.__logger.info(''.join(args)) - - def write_err(self, *args): - self.__logger.error(''.join(args)) - - def flush(self): - pass - - def interactive(self): - return False - - def _readline(self, prompt=''): - return u'' - - def status(self, *msg): - self.__logger.debug(''.join(msg)) - - def warn(self, *msg): - self.__logger.warn(''.join(msg)) - - def note(self, *msg): - self.__logger.info(''.join(msg)) - - def debug(self, *msg): - self.__logger.debug(''.join(msg)) - - def edit(self, text, user): - return text - - def traceback(self, exc=None): - if exc is not None: self.__logger.exception() - - def progress(self, *args, **kwargs): - pass diff --git a/lib/vstorage/tests.py b/lib/vstorage/tests.py deleted file mode 100644 index 25e2b062..00000000 --- a/lib/vstorage/tests.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/python -# -*- 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 -import tempfile -from nose.tools import * -from nose.core import runmodule - -import vstorage - -def clear_directory(top): - for root, dirs, files in os.walk(top, topdown=False): - for name in files: - os.remove(os.path.join(root, name)) - for name in dirs: - os.rmdir(os.path.join(root, name)) - try: - os.removedirs(top) - except OSError: - pass - - -class TestVersionedStorage(object): - def setUp(self): - self.repo_path = tempfile.mkdtemp() - self.repo = vstorage.VersionedStorage(self.repo_path) - - def tearDown(self): - clear_directory(self.repo_path) - - def test_save_text(self): - text = u"test text" - title = u"test title" - author = u"test author" - comment = u"test comment" - - self.repo.save_text( - title=title, - text=text, - author=author, - comment=comment, - parent=None, - ) - - saved_text, rev = self.repo.page_text(title) - assert_equal(saved_text, text) - assert_equal(rev, 0) - - def test_save_text_noparent(self): - text = u"test text" - title = u"test title" - author = u"test author" - comment = u"test comment" - - self.repo.save_text(title=title, - text=text, author=author, - comment=comment, parent=None) - - - saved_text, rev = self.repo.page_text(title) - assert_equal(saved_text, text) - assert_equal(rev, 0) - - def test_save_merge_no_conflict(self): - text = u"test\ntext" - title = u"test title" - author = u"test author" - comment = u"test comment" - self.repo.save_text(title=title, - text=text, author=author, - comment=comment, parent=None) - self.repo.save_text(title=title, - text=text, author=author, - comment=comment, parent=None) - - saved_text, rev = self.repo.page_text(title) - assert_equal(saved_text, text) - assert_equal(rev, 0) - - def test_save_merge_line_conflict(self): - text = u"test\ntest\n" - text1 = u"test\ntext\n" - text2 = u"text\ntest\n" - title = u"test title" - author = u"test author" - comment = u"test comment" - - self.repo.save_text(title=title, - text=text, author=author, - comment=comment, parent=None) - - saved_text, rev = self.repo.page_text(title) - assert_equal(saved_text, text) - assert_equal(rev, 0) - - self.repo.save_text(title=title, - text=text1, author=author, - comment=comment, parent=0) - - saved_text, rev = self.repo.page_text(title) - assert_equal(saved_text, text1) - assert_equal(rev, 1) - - self.repo.save_text(title=title, - text=text2, author=author, - comment=comment, parent=0) - - saved_text, rev = self.repo.page_text(title) - # Other conflict markers placement can also be correct - assert_equal(saved_text, u'''\ -text -test -<<<<<<< local -======= -text ->>>>>>> other -''') - - def test_delete(self): - text = u"text test" - title = u"test title" - author = u"test author" - comment = u"test comment" - self.repo.save_text(title=title, - text=text, author=author, - comment=comment, parent=None) - - ok_(title in self.repo, "Document not in repository.") - - self.repo.delete_page(title, author, comment) - - ok_(title not in self.repo, "Document in repository after delete") - - @raises(vstorage.DocumentNotFound) - def test_document_not_found(self): - self.repo.page_text(u'unknown entity') - - def test_open_existing_repository(self): - self.repo.save_text(title=u'Python!', text=u'ham and spam') - current_repo_revision = self.repo.repo_revision() - same_repo = vstorage.VersionedStorage(self.repo_path) - assert_equal(same_repo.repo_revision(), current_repo_revision) - - def test_history(self): - COMMITS = [ - {"author": "bunny", "text":"1", "comment": "Oh yeah!"}, - {"author": "frank", "text":"2", "comment": "Second is the best!"}, - {"text":"3", "comment": "Third"}, - {"author": "welma", "text":"4", "comment": "Fourth"}, - ] - - for commit in COMMITS: - self.repo.save_text(title=u"Sample", **commit) - - for n, entry in enumerate(reversed(list(self.repo.page_history(u"Sample")))): - assert_equal(entry["version"], n) - assert_equal(entry["author"], COMMITS[n].get("author", "anonymous")) - assert_equal(entry["description"], COMMITS[n]["comment"]) - assert_equal(entry["tag"], []) - - def test_data_revert(self): - COMMITS = [ - {u"title": u"one", "author": "bunny", "text":"1.1", "comment": "1"}, - {u"title": u"one", "author": "frank", "text":"1.2", "comment": "2"}, - {u"title": u"two", "author": "bunny", "text":"2.1", "comment": "3"}, - {u"title": u"one", "author": "frank", "text":"1.3", "comment": "4"}, - ] - - for commit in COMMITS: - self.repo.save_text(**commit) - - # now revert last change on one - self.repo.revert(u"one", 0) - assert_equal(self.repo.page_text(u"one"), (u"1.1", 3)) - assert_equal(self.repo.page_text(u"two"), (u"2.1", 0)) - - self.repo.revert(u"one", 2) - assert_equal(self.repo.page_text(u"one"), (u"1.3", 4)) - -class TestVSTags(object): - - TITLE_1 = "Sample" - - COMMITS = [ - {"author": "bunny", "text":"1", "comment": "Oh yeah!"}, - {"author": "frank", "text":"2", "comment": "Second is the best!"}, - {"text":"3", "comment": "Third"}, - {"author": "welma", "text":"4", "comment": "Fourth"}, - ] - - def setUp(self): - self.repo_path = tempfile.mkdtemp() - self.repo = vstorage.VersionedStorage(self.repo_path) - - # generate some history - for commit in self.COMMITS: - self.repo.save_text(title=u"Sample", **commit) - - # verify - for n, entry in enumerate(reversed(list(self.repo.page_history(self.TITLE_1)))): - assert_equal(entry["tag"], []) - - def tearDown(self): - clear_directory(self.repo_path) - - def test_add_tag(self): - TAG_USER = "mike_the_tagger" - TAG_NAME = "production" - TAG_VERSION = 2 - - # Add tag - self.repo.add_page_tag(self.TITLE_1, TAG_VERSION, TAG_NAME, TAG_USER) - - # check history again - history = list(self.repo.page_history(self.TITLE_1)) - for entry in reversed(history): - if entry["version"] == TAG_VERSION: - assert_equal(entry["tag"], [TAG_NAME]) - else: - assert_equal(entry["tag"], []) - - def test_add_many_tags(self): - TAG_USER = "mike_the_tagger" - tags = [ - (2, "production", "mike"), - (2, "finished", "jeremy"), - (0, "original", "jeremy"), - ] - - for rev, name, user in tags: - self.repo.add_page_tag(self.TITLE_1, rev, name, user) - - # check history again - history = list(self.repo.page_history(self.TITLE_1)) - for entry in reversed(history): - expected = [tag[1] for tag in tags if tag[0] == entry["version"]] - assert_equal(set(entry["tag"]), set(expected)) diff --git a/lib/wlapi/__init__.py b/lib/wlapi/__init__.py deleted file mode 100644 index 3284211a..00000000 --- a/lib/wlapi/__init__.py +++ /dev/null @@ -1,94 +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. -# -""" - Abstraction over API for wolnelektury.pl -""" -import urllib2 -import functools -import django.utils.simplejson as json -import logging -logger = logging.getLogger("fnp.lib.wlapi") - - -class APICallException(Exception): - - def __init__(self, cause=None): - super(Exception, self).__init__() - self.cause = cause - - def __unicode__(self): - return u"%s, cause: %s" % (type(self).__name__, repr(self.cause)) - - def __str__(self): - return self.__unicode__().encode('utf-8') - - -def api_call(path, format="json"): - def wrapper(func): - - @functools.wraps(func) - def wrapped(self, *args, **kwargs): - generator = func(self, *args, **kwargs) - - data = generator.next() - - # prepare request - rq = urllib2.Request(self.base_url + path + ".json") - - # will send POST when there is data, GET otherwise - if data is not None: - rq.add_data(json.dumps(data)) - rq.add_header("Content-Type", "application/json") - - try: - anwser = json.load(self.opener.open(rq)) - return generator.send(anwser) - except StopIteration: - # by default, just return the anwser as a shorthand - return anwser - except urllib2.HTTPError, error: - return self._http_error(error) - except Exception, error: - return self._error(error) - return wrapped - - return wrapper - - -class WLAPI(object): - - def __init__(self, **config_dict): - self.base_url = config_dict['URL'] - self.auth_realm = config_dict['AUTH_REALM'] - self.auth_user = config_dict['AUTH_USER'] - - digest_handler = urllib2.HTTPDigestAuthHandler() - digest_handler.add_password( - realm=self.auth_realm, uri=self.base_url, - user=self.auth_user, passwd=config_dict['AUTH_PASSWD']) - - basic_handler = urllib2.HTTPBasicAuthHandler() - basic_handler.add_password( - realm=self.auth_realm, uri=self.base_url, - user=self.auth_user, passwd=config_dict['AUTH_PASSWD']) - - self.opener = urllib2.build_opener(digest_handler, basic_handler) - - def _http_error(self, error): - message = error.read() - logger.debug("HTTP ERROR: %s", message) - return self._error(message) - - def _error(self, error): - raise APICallException(error) - - @api_call("books") - def list_books(self): - yield - - @api_call("books") - def publish_book(self, document): - yield {"text": document.text, "compressed": False} diff --git a/lib/wlapi/tests.py b/lib/wlapi/tests.py deleted file mode 100644 index a12ab068..00000000 --- a/lib/wlapi/tests.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/python -# -*- 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 nose.core import runmodule - -import wlapi - - -class FakeDocument(): - - def __init__(self): - self.text = "Some Text" - - -class TestWLAPI(object): - - def setUp(self): - self.api = wlapi.WLAPI( - URL="http://localhost:7000/api/", - AUTH_REALM="WL API", - AUTH_USER="platforma", - AUTH_PASSWD="platforma", - ) - - def test_basic_call(self): - assert_equal(self.api.list_books(), []) - - def test_publish_book(self): - self.api.publish_book(FakeDocument()) - -if __name__ == '__main__': - runmodule() diff --git a/redakcja/settings/common.py b/redakcja/settings/common.py index 735743db..0c1b6792 100644 --- a/redakcja/settings/common.py +++ b/redakcja/settings/common.py @@ -86,7 +86,7 @@ MIDDLEWARE_CLASSES = ( AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', - 'django_cas.backends.CASBackend', + 'fnpdjango.auth_backends.AttrCASBackend', ) ROOT_URLCONF = 'redakcja.urls' @@ -116,6 +116,7 @@ INSTALLED_APPS = ( 'djkombu', 'fileupload', 'pipeline', + 'fnpdjango', 'catalogue', 'cover', diff --git a/requirements.txt b/requirements.txt index e741a1b2..99d38f6b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,6 +13,7 @@ httplib2 # oauth2 dependency ## Django Django>=1.5,<1.6 +fnpdjango<0.2 django-pipeline>=1.2,<1.3 django_cas>=2.1,<2.2 sorl-thumbnail>=11.09,<12