From: zuber Date: Tue, 29 Sep 2009 14:40:34 +0000 (+0200) Subject: Merge branch 'master' of stigma.nowoczesnapolska.org.pl:platforma X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/3546a2b2d5f52224bcf95803030611ab505f29cd?hp=ab093330473e6cee879256b9902ce90bd1832c35 Merge branch 'master' of stigma.nowoczesnapolska.org.pl:platforma Conflicts: project/static/js/models.js --- diff --git a/apps/api/forms.py b/apps/api/forms.py index af1b6b5a..d55e6291 100644 --- a/apps/api/forms.py +++ b/apps/api/forms.py @@ -16,14 +16,14 @@ class MergeRequestForm(forms.Form): target_revision = forms.RegexField('[0-9a-f]{40}') # any additional comments that user wants to add to the change - comment = forms.CharField(required=False) + message = forms.CharField(required=False) class DocumentUploadForm(forms.Form): ocr_file = forms.FileField(label='Source OCR file', required=False) ocr_data = forms.CharField(widget=forms.HiddenInput(), required=False) bookname = forms.RegexField(regex=r'[0-9\.\w_-]+', \ - label='Publication name', help_text='Example: slowacki-beniowski') + label='Publication name', help_text='Example: słowacki__beniowski__pieśń_1') generate_dc = forms.BooleanField(required=False, \ initial=True, label=u"Generate DublinCore template") diff --git a/apps/api/handlers/library_handlers.py b/apps/api/handlers/library_handlers.py index 5696cc95..f0ce1863 100644 --- a/apps/api/handlers/library_handlers.py +++ b/apps/api/handlers/library_handlers.py @@ -1,3 +1,4 @@ +import os.path # -*- encoding: utf-8 -*- __author__= "Łukasz Rekucki" @@ -16,15 +17,18 @@ import librarian import librarian.html from librarian import dcparser -from wlrepo import RevisionNotFound, LibraryException, DocumentAlreadyExists -from explorer.models import PullRequest +from wlrepo import * +from explorer.models import PullRequest, GalleryForDocument # internal imports import api.forms as forms import api.response as response -from api.utils import validate_form, hglibrary +from api.utils import validate_form, hglibrary, natural_order from api.models import PartCache +# +import settings + # # Document List Handlers # @@ -52,33 +56,39 @@ class LibraryHandler(BaseHandler): documents = {} for docid in lib.documents(): + docid = docid.decode('utf-8') documents[docid] = { 'url': reverse('document_view', args=[docid]), 'name': docid, 'parts': [] } - related = PartCache.objects.defer('part_id')\ + parts = PartCache.objects.defer('part_id')\ .values_list('part_id', 'document_id').distinct() + + document_tree = dict(documents) - for part, docid in related: + for part, docid in parts: # this way, we won't display broken links if not documents.has_key(part): + print "NOT FOUND:", part continue - child = documents[part] parent = documents[docid] + child = documents[part] + + # not top-level anymore + document_tree.pop(part) + parent['parts'].append(child) + + # sort the right way + + + for doc in documents.itervalues(): + doc['parts'].sort(key=natural_order(lambda d: d['name'])) - if isinstance(parent, dict): # the parent is top-level - documents.pop(part) - parent['parts'].append(child) - documents[part] = child['parts'] - else: # not top-level - parent.append(child) - - return { - 'documents': [d for d in documents.itervalues() if isinstance(d, dict)] - } + return {'documents': sorted(document_tree.itervalues(), + key=natural_order(lambda d: d['name']) ) } @validate_form(forms.DocumentUploadForm, 'POST') @hglibrary @@ -143,9 +153,9 @@ class BasicDocumentHandler(AnonymousBaseHandler): result = { 'name': doc.id, - 'html_url': reverse('dochtml_view', args=[doc.id,doc.revision]), - 'text_url': reverse('doctext_view', args=[doc.id,doc.revision]), - 'dc_url': reverse('docdc_view', args=[doc.id,doc.revision]), + 'html_url': reverse('dochtml_view', args=[doc.id]), + 'text_url': reverse('doctext_view', args=[doc.id]), + 'dc_url': reverse('docdc_view', args=[doc.id]), 'public_revision': doc.revision, } @@ -164,19 +174,24 @@ class DocumentHandler(BaseHandler): try: doc = lib.document(docid) udoc = doc.take(request.user.username) - except RevisionNotFound: - return request.EnityNotFound().django_response() + except RevisionNotFound, e: + return response.EntityNotFound().django_response({ + 'exception': type(e), 'message': e.message}) # is_shared = udoc.ancestorof(doc) # is_uptodate = is_shared or shared.ancestorof(document) result = { 'name': udoc.id, - 'html_url': reverse('dochtml_view', args=[udoc.id,udoc.revision]), - 'text_url': reverse('doctext_view', args=[udoc.id,udoc.revision]), - 'dc_url': reverse('docdc_view', args=[udoc.id,udoc.revision]), + 'html_url': reverse('dochtml_view', args=[udoc.id]), + 'text_url': reverse('doctext_view', args=[udoc.id]), + 'dc_url': reverse('docdc_view', args=[udoc.id]), + 'gallery_url': reverse('docgallery_view', args=[udoc.id]), + 'merge_url': reverse('docmerge_view', args=[udoc.id]), 'user_revision': udoc.revision, - 'public_revision': doc.revision, + 'user_timestamp': udoc.revision.timestamp, + 'public_revision': doc.revision, + 'public_timestamp': doc.revision.timestamp, } return result @@ -189,23 +204,64 @@ class DocumentHandler(BaseHandler): # # class DocumentHTMLHandler(BaseHandler): - allowed_methods = ('GET', 'PUT') + allowed_methods = ('GET') @hglibrary - def read(self, request, docid, revision, lib): + def read(self, request, docid, lib): """Read document as html text""" try: + revision = request.GET.get('revision', 'latest') + if revision == 'latest': document = lib.document(docid) else: document = lib.document_for_rev(revision) + if document.id != docid: + return response.BadRequest().django_response({'reason': 'name-mismatch', + 'message': 'Provided revision refers, to document "%s", but provided "%s"' % (document.id, docid) }) + return librarian.html.transform(document.data('xml'), is_file=False) - except RevisionNotFound: - return response.EntityNotFound().django_response() + except (EntryNotFound, RevisionNotFound), e: + return response.EntityNotFound().django_response({ + 'exception': type(e), 'message': e.message}) + + +# +# Image Gallery +# +from django.core.files.storage import FileSystemStorage + +class DocumentGalleryHandler(BaseHandler): + allowed_methods = ('GET') + + def read(self, request, docid): + """Read meta-data about scans for gallery of this document.""" + galleries = [] + + for assoc in GalleryForDocument.objects.filter(document=docid): + dirpath = os.path.join(settings.MEDIA_ROOT, assoc.subpath) + + if not os.path.isdir(dirpath): + print u"[WARNING]: missing gallery %s" % dirpath + continue + + gallery = {'name': assoc.name, 'pages': []} + + for file in sorted(os.listdir(dirpath), key=natural_order()): + print file + name, ext = os.path.splitext(os.path.basename(file)) + if ext.lower() not in ['.png', '.jpeg', '.jpg']: + print "Ignoring:", name, ext + continue + url = settings.MEDIA_URL + assoc.subpath + u'/' + file.decode('utf-8'); + gallery['pages'].append(url) + + galleries.append(gallery) + return galleries # # Document Text View @@ -214,30 +270,38 @@ class DocumentHTMLHandler(BaseHandler): XINCLUDE_REGEXP = r"""<(?:\w+:)?include\s+[^>]*?href=("|')wlrepo://(?P[^\1]+?)\1\s*[^>]*?>""" # # +# class DocumentTextHandler(BaseHandler): - allowed_methods = ('GET', 'PUT') + allowed_methods = ('GET', 'POST') @hglibrary - def read(self, request, docid, revision, lib): - """Read document as raw text""" + def read(self, request, docid, lib): + """Read document as raw text""" + revision = request.GET.get('revision', 'latest') try: if revision == 'latest': document = lib.document(docid) else: document = lib.document_for_rev(revision) + + if document.id != docid: + return response.BadRequest().django_response({'reason': 'name-mismatch', + 'message': 'Provided revision is not valid for this document'}) # TODO: some finer-grained access control return document.data('xml') - except RevisionNotFound: - return response.EntityNotFound().django_response() + except (EntryNotFound, RevisionNotFound), e: + return response.EntityNotFound().django_response({ + 'exception': type(e), 'message': e.message}) @hglibrary - def update(self, request, docid, revision, lib): + def create(self, request, docid, lib): try: - data = request.PUT['contents'] + data = request.POST['contents'] + revision = request.POST['revision'] - if request.PUT.has_key('message'): - msg = u"$USER$ " + request.PUT['message'] + if request.POST.has_key('message'): + msg = u"$USER$ " + request.POST['message'] else: msg = u"$AUTO$ XML content update." @@ -254,6 +318,8 @@ class DocumentTextHandler(BaseHandler): includes = [m.groupdict()['link'] for m in (re.finditer(\ XINCLUDE_REGEXP, data, flags=re.UNICODE) or []) ] + print "INCLUDES: ", includes + # TODO: provide useful routines to make this simpler def xml_update_action(lib, resolve): try: @@ -289,14 +355,17 @@ class DocumentTextHandler(BaseHandler): "document": ndoc.id, "subview": "xml", "previous_revision": current.revision, - "updated_revision": ndoc.revision, - "url": reverse("doctext_view", args=[ndoc.id, ndoc.revision]) + "revision": ndoc.revision, + 'timestamp': ndoc.revision.timestamp, + "url": reverse("doctext_view", args=[ndoc.id]) }) except Exception, e: if ndoc: lib._rollback() raise e except RevisionNotFound, e: - return response.EntityNotFound().django_response(e) + return response.EntityNotFound(mimetype="text/plain").\ + django_response(e.message) + # # Dublin Core handlers @@ -304,27 +373,37 @@ class DocumentTextHandler(BaseHandler): # @requires librarian # class DocumentDublinCoreHandler(BaseHandler): - allowed_methods = ('GET', 'PUT') + allowed_methods = ('GET', 'POST') @hglibrary - def read(self, request, docid, revision, lib): + def read(self, request, docid, lib): """Read document as raw text""" try: + revision = request.GET.get('revision', 'latest') + if revision == 'latest': doc = lib.document(docid) else: doc = lib.document_for_rev(revision) + + + if document.id != docid: + return response.BadRequest().django_response({'reason': 'name-mismatch', + 'message': 'Provided revision is not valid for this document'}) bookinfo = dcparser.BookInfo.from_string(doc.data('xml')) return bookinfo.serialize() - except RevisionNotFound: - return response.EntityNotFound().django_response() + except (EntryNotFound, RevisionNotFound), e: + return response.EntityNotFound().django_response({ + 'exception': type(e), 'message': e.message}) @hglibrary - def update(self, request, docid, revision, lib): + def create(self, request, docid, lib): try: - bi_json = request.PUT['contents'] - if request.PUT.has_key('message'): + bi_json = request.POST['contents'] + revision = request.POST['revision'] + + if request.POST.has_key('message'): msg = u"$USER$ " + request.PUT['message'] else: msg = u"$AUTO$ Dublin core update." @@ -352,16 +431,16 @@ class DocumentDublinCoreHandler(BaseHandler): "document": ndoc.id, "subview": "dc", "previous_revision": current.revision, - "updated_revision": ndoc.revision + "revision": ndoc.revision, + 'timestamp': ndoc.revision.timestamp, + "url": reverse("docdc_view", args=[ndoc.id]) } except Exception, e: - lib._rollback() + if ndoc: lib._rollback() raise e except RevisionNotFound: return response.EntityNotFound().django_response() - - class MergeHandler(BaseHandler): allowed_methods = ('POST',) @@ -411,7 +490,7 @@ class MergeHandler(BaseHandler): document=docid, source_revision = str(udoc.revision), status="N", - comment = form.cleaned_data['comment'] or '$AUTO$ Document shared.' + comment = form.cleaned_data['message'] or '$AUTO$ Document shared.' ) prq.save() @@ -425,10 +504,10 @@ class MergeHandler(BaseHandler): success, changed = udoc.update(request.user.username) if form.cleaned_data['type'] == 'share': - success, changed = udoc.share(form.cleaned_data['comment']) + success, changed = udoc.share(form.cleaned_data['message']) if not success: - return response.EntityConflict().django_response() + return response.EntityConflict().django_response({}) if not changed: return response.SuccessNoContent().django_response() @@ -439,5 +518,6 @@ class MergeHandler(BaseHandler): "name": udoc.id, "parent_user_resivion": udoc.revision, "parent_revision": doc.revision, - "revision": udoc.revision, + "revision": ndoc.revision, + 'timestamp': ndoc.revision.timestamp, }) diff --git a/apps/api/models.py b/apps/api/models.py index c08f38db..ac694880 100644 --- a/apps/api/models.py +++ b/apps/api/models.py @@ -20,6 +20,9 @@ class PartCache(models.Model): for part in created: me.objects.create(user_id=userid, document_id=docid, part_id=part) + + + diff --git a/apps/api/resources.py b/apps/api/resources.py index 51127e48..103933d5 100644 --- a/apps/api/resources.py +++ b/apps/api/resources.py @@ -19,6 +19,7 @@ document_resource = Resource(dh.DocumentHandler, **authdata) document_text_resource = Resource(dh.DocumentTextHandler, **authdata) document_html_resource = Resource(dh.DocumentHTMLHandler, **authdata) document_dc_resource = Resource(dh.DocumentDublinCoreHandler, **authdata) +document_gallery = Resource(dh.DocumentGalleryHandler, **authdata) document_merge = Resource(dh.MergeHandler, **authdata) import api.handlers.manage_handlers as mh @@ -41,6 +42,7 @@ __all__ = [ 'document_text_resource', 'document_html_resource', 'document_dc_resource', + 'document_gallery', 'document_merge', 'toolbar_buttons', 'scriptlets', diff --git a/apps/api/response.py b/apps/api/response.py index a094f9c1..0d38a3aa 100644 --- a/apps/api/response.py +++ b/apps/api/response.py @@ -22,8 +22,8 @@ class ResponseObject(object): elif self._mime == MIME_JSON: data = json.dumps(body, default=lambda o: repr(o) ) else: - data = u"%s\n%s" % (self.MESSAGE, unicode(body)) - data = data.encode('utf-8') + # data = u"%s\n%s" % (self.MESSAGE, unicode(body)) + data = unicode(body).encode('utf-8') return HttpResponse(content=data, status=self._code, \ content_type=self._mime+'; charset=utf-8' ) diff --git a/apps/api/tests/__init__.py b/apps/api/tests/__init__.py index 751a085c..2044fce7 100644 --- a/apps/api/tests/__init__.py +++ b/apps/api/tests/__init__.py @@ -145,7 +145,7 @@ class SimpleTest(TestCase): @temprepo('simple') - def test_document_text_update(self): + def test_document_text_save(self): self.assertTrue(self.client.login(username='admin', password='admin')) TEXT = u"Ala ma kota i psa" diff --git a/apps/api/urls.py b/apps/api/urls.py index 6a95cf0c..c5b0986d 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -7,7 +7,7 @@ from api.resources import * FORMAT = r"\.(?Pxml|json|yaml)" DOC = r'(?P[^/]+)' -REVISION = r'(?Platest|[0-9a-f]{40})' +# REVISION = r'(?Platest|[0-9a-f]{40})' def urlpath(*args, **kwargs): format = kwargs.get('format', True) @@ -35,29 +35,37 @@ urlpatterns = patterns('', url(urlpath(r'documents'), library_resource, name="document_list_view_withformat"), - url(urlpath(r'documents', DOC), - document_resource, name="document_view_withformat"), + #url(urlpath(r'documents', DOC), + # document_resource, name="document_view_withformat"), url(urlpath(r'documents', DOC, format=False), document_resource, {'emitter_format': 'json'}, name="document_view"), - - url(urlpath(r'documents', DOC, 'text', REVISION, format=False), + + url(urlpath(r'documents', DOC, 'gallery', format=False), + document_gallery, {'emitter_format': 'json'}, + name="docgallery_view"), + + # XML + url(urlpath(r'documents', DOC, 'text', format=False), document_text_resource, {'emitter_format': 'rawxml'}, name="doctext_view"), - url(urlpath(r'documents', DOC, 'html', REVISION, format=False), + # HTML + url(urlpath(r'documents', DOC, 'html', format=False), document_html_resource, {'emitter_format': 'rawhtml'}, name="dochtml_view"), - url(urlpath(r'documents', DOC, 'dc', REVISION), - document_dc_resource, - name="docdc_view_withformat"), + # DC + #url(urlpath(r'documents', DOC, 'dc'), + # document_dc_resource, + # name="docdc_view_withformat"), - url(urlpath(r'documents', DOC, 'dc', REVISION, format=False), + url(urlpath(r'documents', DOC, 'dc', format=False), document_dc_resource, {'emitter_format': 'json'}, name="docdc_view"), + # MERGE url(urlpath(r'documents', DOC, 'revision', format=False), document_merge, {'emitter_format': 'json'}, name="docmerge_view") diff --git a/apps/api/utils.py b/apps/api/utils.py index a52e555b..9b45a320 100644 --- a/apps/api/utils.py +++ b/apps/api/utils.py @@ -11,7 +11,7 @@ from piston.utils import rc import api.response -from wlrepo import MercurialLibrary +import wlrepo import settings class TextEmitter(Emitter): @@ -50,11 +50,19 @@ def validate_form(formclass, source='GET'): def hglibrary(func): @wraps(func) def decorated(self, *args, **kwargs): - l = MercurialLibrary(settings.REPOSITORY_PATH) + l = wlrepo.open_library(settings.REPOSITORY_PATH, 'hg') kwargs['lib'] = l return func(self, *args, **kwargs) return decorated - - - + + + +import re +NAT_EXPR = re.compile(r'(\d+)', re.LOCALE | re.UNICODE) +def natural_order(get_key=lambda x: x): + def getter(key): + key = [int(x) if n%2 else x for (n,x) in enumerate(NAT_EXPR.split(get_key(key))) ] + return key + + return getter diff --git a/apps/explorer/admin.py b/apps/explorer/admin.py index b496893f..034eb3bb 100644 --- a/apps/explorer/admin.py +++ b/apps/explorer/admin.py @@ -5,4 +5,5 @@ import explorer.models admin.site.register(explorer.models.EditorSettings) admin.site.register(explorer.models.EditorPanel) -admin.site.register(explorer.models.PullRequest) \ No newline at end of file +admin.site.register(explorer.models.PullRequest) +admin.site.register(explorer.models.GalleryForDocument) \ No newline at end of file diff --git a/apps/explorer/models.py b/apps/explorer/models.py index 48d0247d..a8a8c3b2 100644 --- a/apps/explorer/models.py +++ b/apps/explorer/models.py @@ -20,7 +20,8 @@ class EditorSettings(models.Model): Przykład: { 'panels': [ - {'name': 'htmleditor', 'ratio': 0.5}, + {'name': 'htmleditor', + 'ratio': 0.5}, {'name': 'gallery', 'ratio': 0.5} ], 'recentFiles': [ @@ -87,16 +88,18 @@ class PullRequest(models.Model): # revision number in which the changes were merged (if any) merged_rev = models.CharField(max_length=40, blank=True, null=True) - def __unicode__(self): return unicode(self.comitter) + u':' + self.document - -def get_image_folders(): - return sorted(fn for fn in os.listdir(os.path.join(settings.MEDIA_ROOT, settings.IMAGE_DIR)) if not fn.startswith('.')) +# Yes, this is intentionally unnormalized ! +class GalleryForDocument(models.Model): + name = models.CharField(max_length=100) + + # directory containing scans under MEDIA_ROOT/ + subpath = models.CharField(max_length=255) -def get_images_from_folder(folder): - return sorted(settings.MEDIA_URL + settings.IMAGE_DIR + u'/' + folder + u'/' + fn.decode('utf-8') for fn - in os.listdir(os.path.join(settings.MEDIA_ROOT, settings.IMAGE_DIR, folder)) - if not fn.decode('utf-8').startswith('.')) + # document associated with the gallery + document = models.CharField(max_length=255) + def __unicode__(self): + return u"%s:%s" % self.subpath, self.document \ No newline at end of file diff --git a/apps/explorer/views.py b/apps/explorer/views.py index eca439ca..23b70592 100644 --- a/apps/explorer/views.py +++ b/apps/explorer/views.py @@ -38,7 +38,7 @@ def file_path(fileid): def with_repo(view): """Open a repository for this view""" def view_with_repo(request, *args, **kwargs): - kwargs['repo'] = wlrepo.MercurialLibrary(settings.REPOSITORY_PATH) + kwargs['repo'] = wlrepo.open_library(settings.REPOSITORY_PATH, 'hg') return view(request, *args, **kwargs) return view_with_repo @@ -65,7 +65,7 @@ def file_list(request, repo): # short-circut the api document list doctree = library_resource.handler.read(request) - print doctree['documents'] + print "DOCTREE:", doctree['documents'] return direct_to_template(request, 'explorer/file_list.html', extra_context={ 'filetree': doctree['documents'], 'bookform': bookform, diff --git a/lib/wlrepo/__init__.py b/lib/wlrepo/__init__.py index 7e878984..ab6f319f 100644 --- a/lib/wlrepo/__init__.py +++ b/lib/wlrepo/__init__.py @@ -108,10 +108,21 @@ class LibraryException(Exception): class RevisionNotFound(LibraryException): def __init__(self, rev): LibraryException.__init__(self, "Revision %r not found." % rev) - pass + +class EntryNotFound(LibraryException): + def __init__(self, rev, entry, guesses=[]): + LibraryException.__init__(self, \ + u"Entry '%s' at revision %r not found. %s" % (entry, rev, \ + (u"Posible values:\n" + u',\n'.join(guesses)) if len(guesses) else u'') ) class DocumentAlreadyExists(LibraryException): pass # import backends to local namespace -from mercurial_backend.library import MercurialLibrary \ No newline at end of file + +def open_library(path, proto, *args, **kwargs): + if proto == 'hg': + import wlrepo.mercurial_backend + return wlrepo.mercurial_backend.MercurialLibrary(path, *args, **kwargs) + + raise NotImplemented() \ No newline at end of file diff --git a/lib/wlrepo/backend_mercurial.py b/lib/wlrepo/backend_mercurial.py deleted file mode 100644 index 35e4442e..00000000 --- a/lib/wlrepo/backend_mercurial.py +++ /dev/null @@ -1,565 +0,0 @@ -## -*- encoding: utf-8 -*- -# -#__author__ = "Łukasz Rekucki" -#__date__ = "$2009-09-18 10:49:24$" -# -#__doc__ = """RAL implementation over Mercurial""" -# -#import mercurial -#from mercurial import localrepo as hglrepo -#from mercurial import ui as hgui -#from mercurial.node import nullid -#import re -#import wlrepo -# -#FILTER = re.compile(r"^pub_(.+)\.xml$", re.UNICODE) -# -#def default_filter(name): -# m = FILTER.match(name) -# if m is not None: -# return name, m.group(1) -# return None -# -#class MercurialLibrary(wlrepo.Library): -# -# def __init__(self, path, maincabinet="default", ** kwargs): -# super(wlrepo.Library, self).__init__( ** kwargs) -# -# self._hgui = hgui.ui() -# self._hgui.config('ui', 'quiet', 'true') -# self._hgui.config('ui', 'interactive', 'false') -# -# import os.path -# self._ospath = self._sanitize_string(os.path.realpath(path)) -# -# maincabinet = self._sanitize_string(maincabinet) -# -# if os.path.isdir(path): -# try: -# self._hgrepo = hglrepo.localrepository(self._hgui, path) -# except mercurial.error.RepoError: -# raise wlrepo.LibraryException("[HGLibrary] Not a valid repository at path '%s'." % path) -# elif kwargs.get('create', False): -# os.makedirs(path) -# try: -# self._hgrepo = hglrepo.localrepository(self._hgui, path, create=1) -# except mercurial.error.RepoError: -# raise wlrepo.LibraryException("[HGLibrary] Can't create a repository on path '%s'." % path) -# else: -# raise wlrepo.LibraryException("[HGLibrary] Can't open a library on path '%s'." % path) -# -# # fetch the main cabinet -# lock = self._hgrepo.lock() -# try: -# btags = self._hgrepo.branchtags() -# -# if not self._has_branch(maincabinet): -# raise wlrepo.LibraryException("[HGLibrary] No branch named '%s' to init main cabinet" % maincabinet) -# -# self._maincab = MercurialCabinet(self, maincabinet) -# finally: -# lock.release() -# -# @property -# def ospath(self): -# return self._ospath -# -# @property -# def main_cabinet(self): -# return self._maincab -# -# def document(self, docid, user, part=None, shelve=None): -# return self.cabinet(docid, user, create=False).retrieve(part=part, shelve=shelve) -# -# def cabinet(self, docid, user, create=False): -# docid = self._sanitize_string(docid) -# user = self._sanitize_string(user) -# -# bname = self._bname(user, docid) -# -# lock = self._lock(True) -# try: -# if self._has_branch(bname): -# return MercurialCabinet(self, doc=docid, user=user) -# -# if not create: -# raise wlrepo.CabinetNotFound(bname) -# -# # check if the docid exists in the main cabinet -# needs_touch = not self._maincab.exists(docid) -# cab = MercurialCabinet(self, doc=docid, user=user) -# -# name, fileid = cab._filename(None) -# -# def cleanup_action(l): -# if needs_touch: -# l._fileopener()(fileid, "w").write('') -# l._fileadd(fileid) -# -# garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)] -# l._filesrm(garbage) -# print "removed: ", garbage -# -# # create the branch -# self._create_branch(bname, before_commit=cleanup_action) -# return MercurialCabinet(self, doc=docid, user=user) -# finally: -# lock.release() -# -# # -# # Private methods -# # -# -# # -# # Locking -# # -# -# def _lock(self, write_mode=False): -# return self._hgrepo.wlock() # no support for read/write mode yet -# -# def _transaction(self, write_mode, action): -# lock = self._lock(write_mode) -# try: -# return action(self) -# finally: -# lock.release() -# -# # -# # Basic repo manipulation -# # -# -# def _checkout(self, rev, force=True): -# return MergeStatus(mercurial.merge.update(self._hgrepo, rev, False, force, None)) -# -# def _merge(self, rev): -# """ Merge the revision into current working directory """ -# return MergeStatus(mercurial.merge.update(self._hgrepo, rev, True, False, None)) -# -# def _common_ancestor(self, revA, revB): -# return self._hgrepo[revA].ancestor(self.repo[revB]) -# -# def _commit(self, message, user=u"library"): -# return self._hgrepo.commit(text=message, user=user) -# -# -# def _fileexists(self, fileid): -# return (fileid in self._hgrepo[None]) -# -# def _fileadd(self, fileid): -# return self._hgrepo.add([fileid]) -# -# def _filesadd(self, fileid_list): -# return self._hgrepo.add(fileid_list) -# -# def _filerm(self, fileid): -# return self._hgrepo.remove([fileid]) -# -# def _filesrm(self, fileid_list): -# return self._hgrepo.remove(fileid_list) -# -# def _filelist(self, filter=default_filter): -# for name in self._hgrepo[None]: -# result = filter(name) -# if result is None: continue -# -# yield result -# -# def _fileopener(self): -# return self._hgrepo.wopener -# -# def _filectx(self, fileid, branchid): -# return self._hgrepo.filectx(fileid, changeid=branchid) -# -# def _changectx(self, nodeid): -# return self._hgrepo.changectx(nodeid) -# -# # -# # BASIC BRANCH routines -# # -# -# def _bname(self, user, docid): -# """Returns a branch name for a given document and user.""" -# docid = self._sanitize_string(docid) -# uname = self._sanitize_string(user) -# return "personal_" + uname + "_file_" + docid; -# -# def _has_branch(self, name): -# return self._hgrepo.branchmap().has_key(self._sanitize_string(name)) -# -# def _branch_tip(self, name): -# name = self._sanitize_string(name) -# return self._hgrepo.branchtags()[name] -# -# def _create_branch(self, name, parent=None, before_commit=None): -# name = self._sanitize_string(name) -# -# if self._has_branch(name): return # just exit -# -# if parent is None: -# parent = self._maincab -# -# parentrev = parent._hgtip() -# -# self._checkout(parentrev) -# self._hgrepo.dirstate.setbranch(name) -# -# if before_commit: before_commit(self) -# -# self._commit("[AUTO] Initial commit for branch '%s'." % name, user='library') -# -# # revert back to main -# self._checkout(self._maincab._hgtip()) -# return self._branch_tip(name) -# -# def _switch_to_branch(self, branchname): -# current = self._hgrepo[None].branch() -# -# if current == branchname: -# return current # quick exit -# -# self._checkout(self._branch_tip(branchname)) -# return branchname -# -# def shelf(self, nodeid=None): -# if nodeid is None: -# nodeid = self._maincab._name -# return MercurialShelf(self, self._changectx(nodeid)) -# -# -# # -# # Utils -# # -# -# @staticmethod -# def _sanitize_string(s): -# if isinstance(s, unicode): -# s = s.encode('utf-8') -# return s -# -#class MercurialCabinet(wlrepo.Cabinet): -# -# def __init__(self, library, branchname=None, doc=None, user=None): -# if doc and user: -# super(MercurialCabinet, self).__init__(library, doc=doc, user=user) -# self._branchname = library._bname(user=user, docid=doc) -# elif branchname: -# super(MercurialCabinet, self).__init__(library, name=branchname) -# self._branchname = branchname -# else: -# raise ValueError("Provide either doc/user or branchname") -# -# def shelf(self): -# return self._library.shelf(self._branchname) -# -# def parts(self): -# return self._execute_in_branch(action=lambda l, c: (e[1] for e in l._filelist())) -# -# def retrieve(self, part=None, shelf=None): -# name, fileid = self._filename(part) -# -# print "Retrieving document %s from cab %s" % (name, self._name) -# -# if fileid is None: -# raise wlrepo.LibraryException("Can't retrieve main document from main cabinet.") -# -# def retrieve_action(l,c): -# if l._fileexists(fileid): -# return MercurialDocument(c, name=name, fileid=fileid) -# print "File %s not found " % fileid -# return None -# -# return self._execute_in_branch(retrieve_action) -# -# def create(self, name, initial_data): -# name, fileid = self._filename(name) -# -# if name is None: -# raise ValueError("Can't create main doc for maincabinet.") -# -# def create_action(l, c): -# if l._fileexists(fileid): -# raise wlrepo.LibraryException("Can't create document '%s' in cabinet '%s' - it already exists" % (fileid, c.name)) -# -# fd = l._fileopener()(fileid, "w") -# fd.write(initial_data) -# fd.close() -# l._fileadd(fileid) -# l._commit("File '%s' created." % fileid) -# return MercurialDocument(c, fileid=fileid, name=name) -# -# return self._execute_in_branch(create_action) -# -# def exists(self, part=None, shelf=None): -# name, filepath = self._filename(part) -# -# if filepath is None: return False -# return self._execute_in_branch(lambda l, c: l._fileexists(filepath)) -# -# def _execute_in_branch(self, action, write=False): -# def switch_action(library): -# old = library._switch_to_branch(self._branchname) -# try: -# return action(library, self) -# finally: -# library._switch_to_branch(old) -# -# return self._library._transaction(write_mode=write, action=switch_action) -# -# -# def _filename(self, docid): -# return self._partname(docid, 'xml') -# -# def _partname(self, docid, part): -# docid = self._library._sanitize_string(part) -# part = self._library._sanitize_string(part) -# -# if part is None: -# part = 'xml' -# -# if self._maindoc == '' and docid is None: -# return None -# -# return 'pub_' + docid + '.' + part -# -# def _fileopener(self): -# return self._library._fileopener() -# -# def _hgtip(self): -# return self._library._branch_tip(self._branchname) -# -# def _filectx(self, fileid): -# return self._library._filectx(fileid, self._branchname) -# -# def ismain(self): -# return (self._library.main_cabinet == self) -# -#class MercurialDocument(wlrepo.Document): -# -# def __init__(self, cabinet, docid): -# super(MercurialDocument, self).__init__(cabinet, name=docid) -# self._opener = self._cabinet._fileopener() -# self._docid = docid -# self._ctxs = {} -# -# def _ctx(self, part): -# if not self._ctxs.has_key(part): -# self._ctxs[part] = self._cabinet._filectx(self._fileid()) -# return self._ctxs[part] -# -# def _fileid(self, part='xml'): -# return self._cabinet._partname(self._docid, part) -# -# def read(self, part='xml'): -# return self._opener(self._ctx(part).path(), "r").read() -# -# def write(self, data, part='xml'): -# return self._opener(self._ctx(part).path(), "w").write(data) -# -# def commit(self, message, user): -# """Commit all parts of the document.""" -# self.library._fileadd(self._fileid) -# self.library._commit(self._fileid, message, user) -# -# def update(self): -# """Update parts of the document.""" -# lock = self.library._lock() -# try: -# if self._cabinet.ismain(): -# return True # always up-to-date -# -# user = self._cabinet.username or 'library' -# mdoc = self.library.document(self._fileid) -# -# mshelf = mdoc.shelf() -# shelf = self.shelf() -# -# if not mshelf.ancestorof(shelf) and not shelf.parentof(mshelf): -# shelf.merge_with(mshelf, user=user) -# -# return True -# finally: -# lock.release() -# -# def share(self, message): -# lock = self.library._lock() -# try: -# print "sharing from", self._cabinet, self._cabinet.username -# -# if self._cabinet.ismain(): -# return True # always shared -# -# if self._cabinet.username is None: -# raise ValueError("Can only share documents from personal cabinets.") -# -# user = self._cabinet.username -# -# main = self.library.shelf() -# local = self.shelf() -# -# no_changes = True -# -# # Case 1: -# # * local -# # | -# # * <- can also be here! -# # /| -# # / | -# # main * * -# # | | -# # The local branch has been recently updated, -# # so we don't need to update yet again, but we need to -# # merge down to default branch, even if there was -# # no commit's since last update -# -# if main.ancestorof(local): -# print "case 1" -# main.merge_with(local, user=user, message=message) -# no_changes = False -# # Case 2: -# # -# # main * * local -# # |\ | -# # | \| -# # | * -# # | | -# # -# # Default has no changes, to update from this branch -# # since the last merge of local to default. -# elif local.has_common_ancestor(main): -# print "case 2" -# if not local.parentof(main): -# main.merge_with(local, user=user, message=message) -# no_changes = False -# -# # Case 3: -# # main * -# # | -# # * <- this case overlaps with previos one -# # |\ -# # | \ -# # | * local -# # | | -# # -# # There was a recent merge to the defaul branch and -# # no changes to local branch recently. -# # -# # Use the fact, that user is prepared to see changes, to -# # update his branch if there are any -# elif local.ancestorof(main): -# print "case 3" -# if not local.parentof(main): -# local.merge_with(main, user=user, message='Local branch update.') -# no_changes = False -# else: -# print "case 4" -# local.merge_with(main, user=user, message='Local branch update.') -# local = self.shelf() -# main.merge_with(local, user=user, message=message) -# -# print "no_changes: ", no_changes -# return no_changes -# finally: -# lock.release() -# -# def shared(self): -# return self.library.main_cabinet.retrieve(self._name) -# -# def exists(self, part='xml'): -# return self._cabinet.exists(self._fileid(part)) -# -# @property -# def size(self): -# return self._filectx.size() -# -# def shelf(self): -# return self._cabinet.shelf() -# -# @property -# def last_modified(self): -# return self._filectx.date() -# -# def __str__(self): -# return u"Document(%s->%s)" % (self._cabinet.name, self._name) -# -# def __eq__(self, other): -# return self._filectx == other._filectx -# -# -# -#class MercurialShelf(wlrepo.Shelf): -# -# def __init__(self, lib, changectx): -# super(MercurialShelf, self).__init__(lib) -# -# if isinstance(changectx, str): -# self._changectx = lib._changectx(changectx) -# else: -# self._changectx = changectx -# -# @property -# def _rev(self): -# return self._changectx.node() -# -# def __str__(self): -# return self._changectx.hex() -# -# def __repr__(self): -# return "MercurialShelf(%s)" % self._changectx.hex() -# -# def ancestorof(self, other): -# nodes = list(other._changectx._parents) -# while nodes[0].node() != nullid: -# v = nodes.pop(0) -# if v == self._changectx: -# return True -# nodes.extend( v._parents ) -# return False -# -# def parentof(self, other): -# return self._changectx in other._changectx._parents -# -# def has_common_ancestor(self, other): -# a = self._changectx.ancestor(other._changectx) -# # print a, self._changectx.branch(), a.branch() -# -# return (a.branch() == self._changectx.branch()) -# -# def merge_with(self, other, user, message): -# lock = self._library._lock(True) -# try: -# self._library._checkout(self._changectx.node()) -# self._library._merge(other._changectx.node()) -# self._library._commit(user=user, message=message) -# finally: -# lock.release() -# -# def __eq__(self, other): -# return self._changectx.node() == other._changectx.node() -# -# -#class MergeStatus(object): -# def __init__(self, mstatus): -# self.updated = mstatus[0] -# self.merged = mstatus[1] -# self.removed = mstatus[2] -# self.unresolved = mstatus[3] -# -# def isclean(self): -# return self.unresolved == 0 -# -#class UpdateStatus(object): -# -# def __init__(self, mstatus): -# self.modified = mstatus[0] -# self.added = mstatus[1] -# self.removed = mstatus[2] -# self.deleted = mstatus[3] -# self.untracked = mstatus[4] -# self.ignored = mstatus[5] -# self.clean = mstatus[6] -# -# def has_changes(self): -# return bool(len(self.modified) + len(self.added) + \ -# len(self.removed) + len(self.deleted)) -# -#__all__ = ["MercurialLibrary"] \ No newline at end of file diff --git a/lib/wlrepo/mercurial_backend/__init__.py b/lib/wlrepo/mercurial_backend/__init__.py index 2d0ce828..9a22395e 100644 --- a/lib/wlrepo/mercurial_backend/__init__.py +++ b/lib/wlrepo/mercurial_backend/__init__.py @@ -28,11 +28,11 @@ class MercurialRevision(wlrepo.Revision): @property def document_name(self): - return self._docname + return self._docname.decode('utf-8') @property def user_name(self): - return self._username + return self._username.decode('utf-8') def hgrev(self): return self._changectx.node() @@ -43,9 +43,16 @@ class MercurialRevision(wlrepo.Revision): def hgbranch(self): return self._changectx.branch() + @property + def timestamp(self): + return self._changectx.date()[0] + def __unicode__(self): return u"%s" % self._changectx.hex() + def __str__(self): + return self.__unicode__().encode('utf-8') + def __repr__(self): return "%s" % self._changectx.hex() diff --git a/lib/wlrepo/mercurial_backend/document.py b/lib/wlrepo/mercurial_backend/document.py index 51a2014c..3f94097a 100644 --- a/lib/wlrepo/mercurial_backend/document.py +++ b/lib/wlrepo/mercurial_backend/document.py @@ -5,13 +5,18 @@ __date__ = "$2009-09-25 09:35:06$" __doc__ = "Module documentation." import wlrepo +import mercurial.error class MercurialDocument(wlrepo.Document): def data(self, entry): - path = self._revision._docname + '.' + entry - return self._library._filectx(path, \ - self._revision.hgrev()).data() + path = self._library._sanitize_string(self.id + u'.' + entry) + try: + return self._library._filectx(path, \ + self._revision.hgrev()).data().decode('utf-8') + except mercurial.error.LookupError, e: + fl = [x.decode('utf-8') for x in self._revision._changectx] + raise wlrepo.EntryNotFound(self._revision, path.decode('utf-8'), fl) def quickwrite(self, entry, data, msg, user=None): user = user or self.owner @@ -32,16 +37,16 @@ class MercurialDocument(wlrepo.Document): f.close() l._fileadd(r(entry)) - return self.invoke_and_commit(write, lambda d: (msg, self.owner)) + return self.invoke_and_commit(write, lambda d: (msg, \ + self._library._sanitize_string(self.owner)) ) - def invoke_and_commit(self, ops, - commit_info): + def invoke_and_commit(self, ops, commit_info): lock = self._library.lock() try: self._library._checkout(self._revision.hgrev()) def entry_path(entry): - return self.id + '.' + entry + return self._library._sanitize_string(self.id + u'.' + entry) ops(self._library, entry_path) message, user = commit_info(self) @@ -184,9 +189,12 @@ class MercurialDocument(wlrepo.Document): finally: lock.release() - def __str__(self): + def __unicode__(self): return u"Document(%s:%s)" % (self.name, self.owner) + def __str__(self): + return self.__unicode__().encode('utf-8') + def __eq__(self, other): return (self._revision == other._revision) and (self.name == other.name) diff --git a/lib/wlrepo/mercurial_backend/library.py b/lib/wlrepo/mercurial_backend/library.py index 7a33bf38..042fda29 100644 --- a/lib/wlrepo/mercurial_backend/library.py +++ b/lib/wlrepo/mercurial_backend/library.py @@ -10,8 +10,8 @@ from mercurial import ui as hgui from mercurial import error import wlrepo -from wlrepo.mercurial_backend.document import MercurialDocument from wlrepo.mercurial_backend import MercurialRevision +from wlrepo.mercurial_backend.document import MercurialDocument class MergeStatus(object): def __init__(self, mstatus): @@ -75,13 +75,14 @@ class MercurialLibrary(wlrepo.Library): @property def ospath(self): - return self._ospath + return self._ospath.decode('utf-8') def document_for_rev(self, revision): if revision is None: raise ValueError("Revision can't be None.") if not isinstance(revision, MercurialRevision): + revision = self._sanitize_string(unicode(revision)) rev = self.get_revision(revision) else: rev = revision @@ -97,25 +98,25 @@ class MercurialLibrary(wlrepo.Library): def get_revision(self, revid): revid = self._sanitize_string(revid) - - ctx = self._changectx(revid) + + try: + ctx = self._changectx(revid) + except mercurial.error.RepoError, e: + raise wlrepo.RevisionNotFound(revid) if ctx is None: - raise RevisionNotFound(revid) + raise wlrepo.RevisionNotFound(revid) if self._revcache.has_key(ctx): return self._revcache[ctx] return MercurialRevision(self, ctx) - def fulldocid(self, docid, user=None): - docid = self._sanitize_string(docid) - user = self._sanitize_string(user) - - fulldocid = '' + def fulldocid(self, docid, user=None): + fulldocid = u'' if user is not None: - fulldocid += '$user:' + user - fulldocid += '$doc:' + docid + fulldocid += u'$user:' + user + fulldocid += u'$doc:' + docid return fulldocid @@ -123,20 +124,20 @@ class MercurialLibrary(wlrepo.Library): try: self._hgrepo[revid] return True - except error.RepoError: + except mercurial.error.RepoError: return False def document_create(self, docid): - docid = self._sanitize_string(docid) + # check if it already exists fullid = self.fulldocid(docid) if self.has_revision(fullid): - raise wlrepo.DocumentAlreadyExists("Document %s already exists!" % docid); + raise wlrepo.DocumentAlreadyExists(u"Document %s already exists!" % docid); # doesn't exist - self._create_branch(fullid) + self._create_branch(self._sanitize_string(fullid)) return self.document_for_rev(fullid) # diff --git a/project/static/js/models.js b/project/static/js/models.js index e141346a..13dc2ea0 100644 --- a/project/static/js/models.js +++ b/project/static/js/models.js @@ -179,6 +179,48 @@ Editor.HTMLModel = Editor.Model.extend({ }); +Editor.ImageGalleryModel = Editor.Model.extend({ + _className: 'Editor.ImageGalleryModel', + serverURL: null, + state: 'empty', + + init: function(serverURL) { + this._super(); + this.set('state', 'empty'); + this.serverURL = serverURL; + // olewać data + this.pages = []; + }, + + load: function() { + if (this.get('state') == 'empty') { + this.set('state', 'loading'); + $.ajax({ + url: this.serverURL, + dataType: 'json', + success: this.loadingSucceeded.bind(this) + }); + } + }, + + loadingSucceeded: function(data) { + if (this.get('state') != 'loading') { + alert('erroneous state:', this.get('state')); + } + + this.set('pages', data[0].pages); + this.set('state', 'synced'); + }, + + set: function(property, value) { + if (property == 'state') { + console.log(this.description(), ':', property, '=', value); + } + return this._super(property, value); + } +}); + + Editor.DocumentModel = Editor.Model.extend({ _className: 'Editor.DocumentModel', data: null, // name, text_url, user_revision, latest_shared_rev, parts_url, dc_url, size, merge_url @@ -208,7 +250,8 @@ Editor.DocumentModel = Editor.Model.extend({ this.set('state', 'synced'); this.contentModels = { 'xml': new Editor.XMLModel(data.text_url, data.user_revision), - 'html': new Editor.HTMLModel(data.html_url, data.user_revision) + 'html': new Editor.HTMLModel(data.html_url, data.user_revision), + 'gallery': new Editor.ImageGalleryModel(data.gallery_url) }; for (var key in this.contentModels) { this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this)); diff --git a/project/templates/explorer/editor.html b/project/templates/explorer/editor.html index 3a64e8d3..3e141679 100644 --- a/project/templates/explorer/editor.html +++ b/project/templates/explorer/editor.html @@ -22,6 +22,7 @@ + @@ -47,6 +48,26 @@
+ +