The actual commit.
authorŁukasz Rekucki <lrekucki@gmail.com>
Tue, 29 Sep 2009 14:13:26 +0000 (16:13 +0200)
committerŁukasz Rekucki <lrekucki@gmail.com>
Tue, 29 Sep 2009 14:13:26 +0000 (16:13 +0200)
16 files changed:
apps/api/forms.py
apps/api/handlers/library_handlers.py
apps/api/models.py
apps/api/resources.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/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
     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_-]+',  \
 
 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")
     
     generate_dc = forms.BooleanField(required=False, \
         initial=True, label=u"Generate DublinCore template")
index d2c6ec3..2170b44 100644 (file)
@@ -1,3 +1,4 @@
+import os.path
 # -*- encoding: utf-8 -*-
 
 __author__= "Łukasz Rekucki"
 # -*- encoding: utf-8 -*-
 
 __author__= "Łukasz Rekucki"
@@ -16,15 +17,18 @@ import librarian
 import librarian.html
 from librarian import dcparser
 
 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
 
 # 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
 
 from api.models import PartCache
 
+#
+import settings
+
 #
 # Document List Handlers
 #
 #
 # Document List Handlers
 #
@@ -76,8 +80,15 @@ class LibraryHandler(BaseHandler):
             # not top-level anymore
             document_tree.pop(part)
             parent['parts'].append(child)
             # 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']))
             
             
-        return {'documents': sorted(document_tree.values()) }
+        return {'documents': sorted(document_tree.itervalues(),
+            key=natural_order(lambda d: d['name']) ) }
 
     @validate_form(forms.DocumentUploadForm, 'POST')
     @hglibrary
 
     @validate_form(forms.DocumentUploadForm, 'POST')
     @hglibrary
@@ -163,8 +174,9 @@ class DocumentHandler(BaseHandler):
         try:
             doc = lib.document(docid)
             udoc = doc.take(request.user.username)
         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)
 
         # is_shared = udoc.ancestorof(doc)
         # is_uptodate = is_shared or shared.ancestorof(document)
@@ -174,9 +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]),
             '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('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,
             'user_revision': udoc.revision,
-            'public_revision': doc.revision,            
+            'user_timestamp': udoc.revision.timestamp,
+            'public_revision': doc.revision,
+            'public_timestamp': doc.revision.timestamp,
         }       
 
         return result
         }       
 
         return result
@@ -189,7 +204,7 @@ class DocumentHandler(BaseHandler):
 #
 #
 class DocumentHTMLHandler(BaseHandler):
 #
 #
 class DocumentHTMLHandler(BaseHandler):
-    allowed_methods = ('GET', 'PUT')
+    allowed_methods = ('GET')
 
     @hglibrary
     def read(self, request, docid, revision, lib):
 
     @hglibrary
     def read(self, request, docid, revision, lib):
@@ -200,9 +215,51 @@ class DocumentHTMLHandler(BaseHandler):
             else:
                 document = lib.document_for_rev(revision)
 
             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)
             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
 
 #
 # Document Text View
@@ -223,11 +280,16 @@ class DocumentTextHandler(BaseHandler):
                 document = lib.document(docid)
             else:
                 document = lib.document_for_rev(revision)
                 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')
             
             # 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):
 
     @hglibrary
     def update(self, request, docid, revision, lib):
@@ -289,7 +351,8 @@ class DocumentTextHandler(BaseHandler):
                     "document": ndoc.id,
                     "subview": "xml",
                     "previous_revision": current.revision,
                     "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:
                     "url": reverse("doctext_view", args=[ndoc.id, ndoc.revision])
                 })
             except Exception, e:
@@ -316,11 +379,17 @@ class DocumentDublinCoreHandler(BaseHandler):
                 doc = lib.document(docid)
             else:
                 doc = lib.document_for_rev(revision)
                 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()
             
             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):
 
     @hglibrary
     def update(self, request, docid, revision, lib):
@@ -354,7 +423,8 @@ class DocumentDublinCoreHandler(BaseHandler):
                     "document": ndoc.id,
                     "subview": "dc",
                     "previous_revision": current.revision,
                     "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:
                     "url": reverse("docdc_view", args=[ndoc.id, ndoc.revision])
                 }
             except Exception, e:
@@ -412,7 +482,7 @@ class MergeHandler(BaseHandler):
                 document=docid,
                 source_revision = str(udoc.revision),
                 status="N",
                 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()
             )
 
             prq.save()
@@ -426,10 +496,10 @@ class MergeHandler(BaseHandler):
             success, changed = udoc.update(request.user.username)
 
         if form.cleaned_data['type'] == 'share':
             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:
 
         if not success:
-            return response.EntityConflict().django_response()
+            return response.EntityConflict().django_response({})
 
         if not changed:
             return response.SuccessNoContent().django_response()
 
         if not changed:
             return response.SuccessNoContent().django_response()
@@ -440,5 +510,6 @@ class MergeHandler(BaseHandler):
             "name": udoc.id,
             "parent_user_resivion": udoc.revision,
             "parent_revision": doc.revision,
             "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)
 
         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_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
 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_text_resource',
     'document_html_resource',
     'document_dc_resource',
+    'document_gallery',
     'document_merge',
     'toolbar_buttons',
     'scriptlets',
     'document_merge',
     'toolbar_buttons',
     'scriptlets',
index 751a085..2044fce 100644 (file)
@@ -145,7 +145,7 @@ class SimpleTest(TestCase):
 
 
     @temprepo('simple')
 
 
     @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"
 
         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, 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"),
 
     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"),
 
     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"),
     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"),
 
         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")
 
     url(urlpath(r'documents', DOC, 'revision', format=False),
         document_merge, {'emitter_format': 'json'}, name="docmerge_view")
 
index 93e54be..9b45a32 100644 (file)
@@ -11,7 +11,7 @@ from piston.utils import rc
 
 import api.response
 
 
 import api.response
 
-from wlrepo import MercurialLibrary
+import wlrepo
 import settings
 
 class TextEmitter(Emitter):
 import settings
 
 class TextEmitter(Emitter):
@@ -50,8 +50,19 @@ def validate_form(formclass, source='GET'):
 def hglibrary(func):
     @wraps(func)
     def decorated(self, *args, **kwargs):
 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)
         kwargs['lib'] = l
         return func(self, *args, **kwargs)
-    return decorated                   
+    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.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': [
     Przykład:
     {
         'panels': [
-            {'name': 'htmleditor', 'ratio': 0.5},
+            {'name': 'htmleditor',
+            'ratio': 0.5},
             {'name': 'gallery', 'ratio': 0.5}
         ],
         'recentFiles': [
             {'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)
 
     # 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 __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 28082e6..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):          
 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
 
         return view(request, *args, **kwargs)
     return view_with_repo
 
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)
 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
 
 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
index 2d0ce82..9a22395 100644 (file)
@@ -28,11 +28,11 @@ class MercurialRevision(wlrepo.Revision):
         
     @property
     def document_name(self):
         
     @property
     def document_name(self):
-        return self._docname
+        return self._docname.decode('utf-8')
 
     @property
     def user_name(self):
 
     @property
     def user_name(self):
-        return self._username
+        return self._username.decode('utf-8')
 
     def hgrev(self):
         return self._changectx.node()
 
     def hgrev(self):
         return self._changectx.node()
@@ -43,9 +43,16 @@ class MercurialRevision(wlrepo.Revision):
     def hgbranch(self):
         return self._changectx.branch()
 
     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 __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()
 
     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
 __doc__ = "Module documentation."
 
 import wlrepo
+import mercurial.error
 
 class MercurialDocument(wlrepo.Document):
 
     def data(self, entry):
 
 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
 
     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))            
 
             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):
         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)
             
             ops(self._library, entry_path)
             message, user = commit_info(self)
@@ -184,9 +189,12 @@ class MercurialDocument(wlrepo.Document):
         finally:
             lock.release()     
 
         finally:
             lock.release()     
 
-    def __str__(self):
+    def __unicode__(self):
         return u"Document(%s:%s)" % (self.name, self.owner)
 
         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)
 
     def __eq__(self, other):
         return (self._revision == other._revision) and (self.name == other.name)
 
index 648ebe9..042fda2 100644 (file)
@@ -10,8 +10,8 @@ from mercurial import ui as hgui
 from mercurial import error
 import wlrepo
 
 from mercurial import error
 import wlrepo
 
-from wlrepo.mercurial_backend.document import MercurialDocument
 from wlrepo.mercurial_backend import MercurialRevision
 from wlrepo.mercurial_backend import MercurialRevision
+from wlrepo.mercurial_backend.document import MercurialDocument
 
 class MergeStatus(object):
     def __init__(self, mstatus):
 
 class MergeStatus(object):
     def __init__(self, mstatus):
@@ -75,13 +75,14 @@ class MercurialLibrary(wlrepo.Library):
 
     @property
     def ospath(self):
 
     @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):
 
     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       
             rev = self.get_revision(revision)
         else:
             rev = revision       
@@ -111,14 +112,11 @@ class MercurialLibrary(wlrepo.Library):
 
         return MercurialRevision(self, 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:
         if user is not None:
-            fulldocid += '$user:' + user
-        fulldocid += '$doc:' + docid
+            fulldocid += u'$user:' + user
+        fulldocid += u'$doc:' + docid
         return fulldocid
 
 
         return fulldocid
 
 
@@ -130,16 +128,16 @@ class MercurialLibrary(wlrepo.Library):
             return False
 
     def document_create(self, docid):
             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):
         
         # 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
 
         # doesn't exist
-        self._create_branch(fullid)
+        self._create_branch(self._sanitize_string(fullid))
         return self.document_for_rev(fullid)
 
     #
         return self.document_for_rev(fullid)
 
     #
index 9542eac..d0de300 100644 (file)
@@ -172,6 +172,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
 Editor.DocumentModel = Editor.Model.extend({
   _className: 'Editor.DocumentModel',
   data: null, // name, text_url, latest_rev, latest_shared_rev, parts_url, dc_url, size
@@ -201,7 +243,8 @@ Editor.DocumentModel = Editor.Model.extend({
     this.set('state', 'synced');
     this.contentModels = {
       'xml': new Editor.XMLModel(data.text_url),
     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));
     };
     for (var key in this.contentModels) {
       this.contentModels[key].addObserver(this, 'state', this.contentModelStateChanged.bind(this));
index d075678..7cb23a7 100644 (file)
@@ -18,6 +18,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/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>
        <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>
                <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">
        
        <script type="text/html" charset="utf-8" id="button-toolbar-view-template">
                <div class="buttontoolbarview">