Merge branch 'master' of stigma:platforma
authorŁukasz Rekucki <lrekucki@gmail.com>
Tue, 29 Sep 2009 14:13:49 +0000 (16:13 +0200)
committerŁukasz Rekucki <lrekucki@gmail.com>
Tue, 29 Sep 2009 14:13:49 +0000 (16:13 +0200)
18 files changed:
apps/api/forms.py
apps/api/handlers/library_handlers.py
apps/api/models.py
apps/api/resources.py
apps/api/response.py
apps/api/tests/__init__.py
apps/api/urls.py
apps/api/utils.py
apps/explorer/admin.py
apps/explorer/models.py
apps/explorer/views.py
lib/wlrepo/__init__.py
lib/wlrepo/backend_mercurial.py [deleted file]
lib/wlrepo/mercurial_backend/__init__.py
lib/wlrepo/mercurial_backend/document.py
lib/wlrepo/mercurial_backend/library.py
project/static/js/models.js
project/templates/explorer/editor.html

index af1b6b5..d55e629 100644 (file)
@@ -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")
index 5696cc9..2170b44 100644 (file)
@@ -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
@@ -164,8 +174,9 @@ 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)
@@ -175,8 +186,12 @@ class DocumentHandler(BaseHandler):
             '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]),
+            '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,7 +204,7 @@ class DocumentHandler(BaseHandler):
 #
 #
 class DocumentHTMLHandler(BaseHandler):
-    allowed_methods = ('GET', 'PUT')
+    allowed_methods = ('GET')
 
     @hglibrary
     def read(self, request, docid, revision, lib):
@@ -200,12 +215,51 @@ class DocumentHTMLHandler(BaseHandler):
             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,6 +268,7 @@ class DocumentHTMLHandler(BaseHandler):
 XINCLUDE_REGEXP = r"""<(?:\w+:)?include\s+[^>]*?href=("|')wlrepo://(?P<link>[^\1]+?)\1\s*[^>]*?>"""
 #
 #
+#
 class DocumentTextHandler(BaseHandler):
     allowed_methods = ('GET', 'PUT')
 
@@ -225,11 +280,16 @@ class DocumentTextHandler(BaseHandler):
                 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):
@@ -254,6 +314,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 +351,17 @@ class DocumentTextHandler(BaseHandler):
                     "document": ndoc.id,
                     "subview": "xml",
                     "previous_revision": current.revision,
-                    "updated_revision": ndoc.revision,
+                    "revision": ndoc.revision,
+                    'timestamp': ndoc.revision.timestamp,
                     "url": reverse("doctext_view", args=[ndoc.id, ndoc.revision])
                 })
             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
@@ -314,11 +379,17 @@ class DocumentDublinCoreHandler(BaseHandler):
                 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):
@@ -352,16 +423,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, ndoc.revision])
                 }
             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 +482,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 +496,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 +510,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,
         })
index c08f38d..ac69488 100644 (file)
@@ -20,6 +20,9 @@ class PartCache(models.Model):
 
         for part in created:
             me.objects.create(user_id=userid, document_id=docid, part_id=part)
+
+
+            
         
 
         
index 51127e4..103933d 100644 (file)
@@ -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',
index a094f9c..0d38a3a 100644 (file)
@@ -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' )        
index 751a085..2044fce 100644 (file)
@@ -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"
 
index 6a95cf0..18fc02c 100644 (file)
@@ -41,15 +41,22 @@ urlpatterns = patterns('',
     url(urlpath(r'documents', DOC, format=False),
         document_resource, {'emitter_format': 'json'},
         name="document_view"),
-    
+
+    url(urlpath(r'documents', DOC, 'gallery', format=False),
+        document_gallery, {'emitter_format': 'json'},
+        name="docgallery_view"),
+
+    # XML    
     url(urlpath(r'documents', DOC, 'text', REVISION, format=False),
         document_text_resource, {'emitter_format': 'rawxml'},
         name="doctext_view"),
 
+    # HTML
     url(urlpath(r'documents', DOC, 'html', REVISION, format=False),
         document_html_resource, {'emitter_format': 'rawhtml'},
         name="dochtml_view"),
 
+    # DC
     url(urlpath(r'documents', DOC, 'dc', REVISION),
         document_dc_resource,
         name="docdc_view_withformat"),
@@ -58,6 +65,7 @@ urlpatterns = patterns('',
         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")
 
index a52e555..9b45a32 100644 (file)
@@ -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
 
index b496893..034eb3b 100644 (file)
@@ -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
index 48d0247..5181e16 100644 (file)
@@ -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,15 @@ 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('.'))
-
 
-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('.'))
+# 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)
 
+    # document associated with the gallery
+    document = models.CharField(max_length=255)
index eca439c..23b7059 100644 (file)
@@ -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,
index 7e87898..ab6f319 100644 (file)
@@ -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 (file)
index 35e4442..0000000
+++ /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
index 2d0ce82..9a22395 100644 (file)
@@ -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()
 
index 51a2014..3f94097 100644 (file)
@@ -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)
 
index 7a33bf3..042fda2 100644 (file)
@@ -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)
 
     #
index 75720a0..62d1c78 100644 (file)
@@ -174,6 +174,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.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, latest_rev, latest_shared_rev, parts_url, dc_url, size
@@ -203,7 +245,8 @@ Editor.DocumentModel = Editor.Model.extend({
     this.set('state', 'synced');
     this.contentModels = {
       'xml': new Editor.XMLModel(data.text_url),
-      'html': new Editor.HTMLModel(data.html_url)
+      'html': new Editor.HTMLModel(data.html_url),
+      'gallery': new Editor.ImageGalleryModel(data.gallery_url)
     };
     for (var key in this.contentModels) {
       this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this));
index 3a64e8d..de1e242 100644 (file)
@@ -22,6 +22,7 @@
        <script src="{{STATIC_URL}}js/views/split.js" type="text/javascript" charset="utf-8"></script>
        <script src="{{STATIC_URL}}js/views/xml.js" type="text/javascript" charset="utf-8"></script>
        <script src="{{STATIC_URL}}js/views/html.js" type="text/javascript" charset="utf-8"></script>
+        <script src="{{STATIC_URL}}js/views/gallery.js" type="text/javascript" charset="utf-8"></script>
        <script src="{{STATIC_URL}}js/views/panel_container.js" type="text/javascript" charset="utf-8"></script>
        
        <script src="{{STATIC_URL}}js/models.js" type="text/javascript" charset="utf-8"></script>
                <div class="htmlview">
                </div>
        </script>
+
+        <script type="text/html" charset="utf-8" id="image-gallery-view-template">
+       <div class="image-gallery-view-template">
+
+        <div class="image-gallery-panel-header">
+        <p>
+        <button type="button" class="image-gallery-prev-button">Previous</button>
+        <input type="input" class="image-gallery-current-page" />
+        <button type="button" class="image-gallery-next-button">Next</button>
+        </p>
+        </div>
+
+        <div>
+            <% for (page in panels) { %>
+                <p>strona</p>
+            <% }; %>
+       </div>
+        
+        </div>
+       </script>
        
        <script type="text/html" charset="utf-8" id="button-toolbar-view-template">
                <div class="buttontoolbarview">