From: Łukasz Rekucki Date: Fri, 25 Sep 2009 15:18:54 +0000 (+0200) Subject: Updated API tests. X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/352b8591bd1c7163835a6fa1db34d3e2861c1071?ds=inline Updated API tests. Handlers refactor. Addded Toolbar API. --- diff --git a/apps/api/handlers.py b/apps/api/handlers.py deleted file mode 100644 index 9a54ce8c..00000000 --- a/apps/api/handlers.py +++ /dev/null @@ -1,203 +0,0 @@ -from piston.handler import BaseHandler, AnonymousBaseHandler -from piston.utils import rc, validate - -import settings -import librarian -import api.forms as forms -from datetime import date - -from django.core.urlresolvers import reverse -from wlrepo import MercurialLibrary, CabinetNotFound - -from librarian import dcparser - -# -# Document List Handlers -# -class BasicLibraryHandler(AnonymousBaseHandler): - allowed_methods = ('GET',) - - def read(self, request): - """Return the list of documents.""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - cab = lib.main_cabinet - - document_list = [{ - 'url': reverse('document_view', args=[docid]), - 'name': docid } for docid in cab.documents() ] - - return { - 'cabinet': cab.name, - 'latest_rev': cab.shelf(), - 'documents' : document_list } - -class LibraryHandler(BaseHandler): - allowed_methods = ('GET', 'POST') - anonymous = BasicLibraryHandler - - def read(self, request): - """Return the list of documents.""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - cab = lib.main_cabinet - - document_list = [{ - 'url': reverse('document_view', args=[docid]), - 'name': docid } for docid in cab.documents() ] - - return { - 'cabinet': cab.name, - 'latest_rev': cab.shelf(), - 'documents' : document_list } - - def create(self, request): - """Create a new document.""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - cab = lib.main_cabinet - - form = forms.DocumentUploadForm(request.POST, request.FILES) - if not form.is_valid(): - return rc.BAD_REQUEST - - f = request.FILES['ocr'] - data = f.read().decode('utf-8') - - if form.cleaned_data['generate_dc']: - data = librarian.wrap_text(data, unicode(date.today())) - - # TODO: what if the file exists ? - doc = cab.create(form.cleaned_data['bookname'], initial_data=data) - - return { - 'url': reverse('document_view', args=[doc.name]), - 'name': doc.name, - 'size': doc.size, - 'revision': doc.shelf() } - -# -# Document Handlers -# -class BasicDocumentHandler(AnonymousBaseHandler): - allowed_methods = ('GET',) - - def read(self, request, docid): - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - - opts = forms.DocumentGetForm(request.GET) - if not opts.is_valid(): - return rc.BAD_REQUEST - - document = lib.main_cabinet.retrieve(docid) - - result = { - 'name': document.name, - 'size': document.size, - 'text_url': reverse('doctext_view', args=[docid]), - #'dc_url': reverse('docdc_view', docid=document.name), - #'parts_url': reverse('docparts_view', docid=document.name), - 'latest_rev': document.shelf(), - } - - if request.GET.get('with_part', 'no') == 'yes': - result['parts'] = document.parts() - - return result - -# -# Document Meta Data -# -class DocumentHandler(BaseHandler): - allowed_methods = ('GET', 'PUT') - anonymous = BasicDocumentHandler - - def read(self, request, docid): - """Read document's meta data""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - - opts = forms.DocumentGetForm(request.GET) - if not opts.is_valid(): - return rc.BAD_REQUEST - - document = lib.cabinet(docid, request.user.username, \ - create=opts.cleaned_data['autocabinet'] ).retrieve() - - if not document: - return rc.NOT_HERE - - shared = lib.main_cabinet.retrieve(docid) - - is_shared = document.ancestorof(shared) - # is_uptodate = is_shared or shared.ancestorof(document) - - result = { - 'name': document.name, - 'size': document.size, - 'text_url': reverse('doctext_view', args=[docid]), - 'dc_url': reverse('docdc_view', args=[docid]), - 'parts_url': reverse('docparts_view', args=[docid]), - 'latest_rev': document.shelf(), - 'latest_shared_rev': shared.shelf(), - 'shared': is_shared, - # 'up_to_date': is_uptodate, - } - - #if request.GET.get('with_part', 'no') == 'yes': - # result['parts'] = document.parts() - - return result - -# -# Document Text View -# -class DocumentTextHandler(BaseHandler): - allowed_methods = ('GET', 'PUT') - - def read(self, request, docid): - """Read document as raw text""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - try: - # latest rev - # comment - return lib.document(docid, request.user.username).read() - except CabinetNotFound: - return rc.NOT_HERE - - def update(self, request, docid): - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - try: - # check latest REV - data = request.PUT['contents'] - lib.document(docid, request.user.username).write(data) - return rc.ALL_OK - except (CabinetNotFound, KeyError): - return rc.NOT_HERE - - -# -# Dublin Core handlers -# -# @requires librarian -# -class DocumentDublinCoreHandler(BaseHandler): - allowed_methods = ('GET', 'PUT') - - def read(self, request, docid): - """Read document as raw text""" - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - try: - doc = lib.document(docid, request.user.username) - - # TODO: RAL:document should support file-like ops - bookinfo = dcparser.BookInfo.from_string(doc.read()) - return bookinfo.serialize() - except CabinetNotFound: - return rc.NOT_HERE - - def update(self, request, docid): - lib = MercurialLibrary(path=settings.REPOSITORY_PATH) - try: - data = request.PUT['contents'] - lib.document(docid, request.user.username).write(data) - return rc.ALL_OK - except (CabinetNotFound, KeyError): - return rc.NOT_HERE - diff --git a/apps/api/handlers/__init__.py b/apps/api/handlers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/api/handlers/library_handlers.py b/apps/api/handlers/library_handlers.py new file mode 100644 index 00000000..b47d41cc --- /dev/null +++ b/apps/api/handlers/library_handlers.py @@ -0,0 +1,219 @@ +# -*- encoding: utf-8 -*- + +__author__= "Łukasz Rekucki" +__date__ = "$2009-09-25 15:49:50$" +__doc__ = "Module documentation." + +from piston.handler import BaseHandler, AnonymousBaseHandler +from piston.utils import rc + +import settings +import librarian +import api.forms as forms +from datetime import date + +from django.core.urlresolvers import reverse +from wlrepo import MercurialLibrary, RevisionNotFound + +from librarian import dcparser + +# +# Document List Handlers +# +class BasicLibraryHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + + def read(self, request): + """Return the list of documents.""" + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + + document_list = [{ + 'url': reverse('document_view', args=[docid]), + 'name': docid } for docid in lib.documents() ] + + return {'documents' : document_list} + +class LibraryHandler(BaseHandler): + allowed_methods = ('GET', 'POST') + anonymous = BasicLibraryHandler + + def read(self, request): + """Return the list of documents.""" + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + + document_list = [{ + 'url': reverse('document_view', args=[docid]), + 'name': docid } for docid in lib.documents() ] + + return {'documents' : document_list } + + def create(self, request): + """Create a new document.""" + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + + form = forms.DocumentUploadForm(request.POST, request.FILES) + if not form.is_valid(): + return rc.BAD_REQUEST + + f = request.FILES['ocr'] + data = f.read().decode('utf-8') + + if form.cleaned_data['generate_dc']: + data = librarian.wrap_text(data, unicode(date.today())) + + # TODO: what if the file exists ? + doc = lib.document_create(form.cleaned_data['bookname']) + doc.quickwrite('xml', data, '$AUTO$ XML data uploaded.', + user=request.user.username) + + return { + 'url': reverse('document_view', args=[doc.id]), + 'name': doc.id, + 'revision': doc.revision } + +# +# Document Handlers +# +class BasicDocumentHandler(AnonymousBaseHandler): + allowed_methods = ('GET',) + + def read(self, request, docid): + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + + opts = forms.DocumentGetForm(request.GET) + if not opts.is_valid(): + return rc.BAD_REQUEST + + doc = lib.document(docid) + + result = { + 'name': doc.id, + 'text_url': reverse('doctext_view', args=[doc.id]), + 'dc_url': reverse('docdc_view', docid=doc.id), + 'latest_rev': doc.revision, + } + + return result + +# +# Document Meta Data +# +class DocumentHandler(BaseHandler): + allowed_methods = ('GET', 'PUT') + anonymous = BasicDocumentHandler + + def read(self, request, docid): + """Read document's meta data""" + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + + opts = forms.DocumentGetForm(request.GET) + if not opts.is_valid(): + return rc.BAD_REQUEST + + try: + doc = lib.document(docid) + udoc = doc.take(request.user.username) + except RevisionNotFound: + return rc.NOT_HERE + + # is_shared = udoc.ancestorof(doc) + # is_uptodate = is_shared or shared.ancestorof(document) + + result = { + 'name': udoc.id, + 'text_url': reverse('doctext_view', args=[udoc.id]), + 'dc_url': reverse('docdc_view', args=[udoc.id]), + 'parts_url': reverse('docparts_view', args=[udoc.id]), + 'latest_rev': udoc.revision, + 'latest_shared_rev': doc.revision, + # 'shared': is_shared, + # 'up_to_date': is_uptodate, + } + + #if request.GET.get('with_part', 'no') == 'yes': + # result['parts'] = document.parts() + + return result + +# +# Document Text View +# +class DocumentTextHandler(BaseHandler): + allowed_methods = ('GET', 'PUT') + + def read(self, request, docid): + """Read document as raw text""" + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + try: + return lib.document(docid, request.user.username).data('xml') + except RevisionNotFound: + return rc.NOT_HERE + + def update(self, request, docid): + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + try: + data = request.PUT['contents'] + prev = request.PUT['revision'] + + if request.PUT.has_key('message'): + msg = u"$USER$ " + request.PUT['message'] + else: + msg = u"$AUTO$ XML content update." + + current = lib.document(docid, request.user.username) + orig = lib.document_for_rev(prev) + + if current != orig: + return rc.DUPLICATE_ENTRY + + doc.quickwrite('xml', data, msg) + + return rc.ALL_OK + except (RevisionNotFound, KeyError): + return rc.NOT_HERE + +# +# Dublin Core handlers +# +# @requires librarian +# +class DocumentDublinCoreHandler(BaseHandler): + allowed_methods = ('GET', 'PUT') + + def read(self, request, docid): + """Read document as raw text""" + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + try: + doc = lib.document(docid, request.user.username).data('xml') + bookinfo = dcparser.BookInfo.from_string(doc.read()) + + return bookinfo.serialize() + except RevisionNotFound: + return rc.NOT_HERE + + def update(self, request, docid): + lib = MercurialLibrary(path=settings.REPOSITORY_PATH) + try: + bi_json = request.PUT['contents'] + prev = request.PUT['revision'] + if request.PUT.has_key('message'): + msg = u"$USER$ " + request.PUT['message'] + else: + msg = u"$AUTO$ Dublin core update." + + current = lib.document(docid, request.user.username) + orig = lib.document_for_rev(prev) + + if current != orig: + return rc.DUPLICATE_ENTRY + + xmldoc = parser.WLDocument.from_string(current.data('xml')) + document.book_info = dcparser.BookInfo.from_json(bi_json) + + # zapisz + current.quickwrite('xml', document.serialize().encode('utf-8'),\ + message=msg, user=request.user.username) + + return rc.ALL_OK + except (RevisionNotFound, KeyError): + return rc.NOT_HERE diff --git a/apps/api/handlers/toolbar_handlers.py b/apps/api/handlers/toolbar_handlers.py new file mode 100644 index 00000000..be773590 --- /dev/null +++ b/apps/api/handlers/toolbar_handlers.py @@ -0,0 +1,32 @@ +# -*- encoding: utf-8 -*- + +__author__= "Łukasz Rekucki" +__date__ = "$2009-09-25 15:55:33$" +__doc__ = "Module documentation." + +from django.views.generic.simple import direct_to_template +from piston.handler import BaseHandler +from piston.utils import rc + +import settings + +import toolbar.models + +class ToolbarHandler(BaseHandler): + allowed_methods = ('GET',) + + def read(self, request): + groups = toolbar.models.ButtonGroup.objects.all() + return [ {'name': g.name, 'position': g.position,\ + 'buttons': g.button_set.all()} for g in groups ] + + +class ScriptletsHandler(BaseHandler): + allowed_methods = ('GET',) + + def read(self, request): + scriptlets = toolbar.models.Scriptlet.objects.all() + + return direct_to_template(request, 'toolbar_api/scriptlets.js', + extra_context = {'scriptlets': scriptlets }, + mimetype='text/javascript' ) \ No newline at end of file diff --git a/apps/api/resources.py b/apps/api/resources.py new file mode 100644 index 00000000..d45fa1d7 --- /dev/null +++ b/apps/api/resources.py @@ -0,0 +1,39 @@ +# -*- encoding: utf-8 -*- + +__author__= "Łukasz Rekucki" +__date__ = "$2009-09-25 15:53:00$" +__doc__ = "Module documentation." + +from piston.resource import Resource +from api.utils import DjangoAuth + + + + +authdata = {'authentication': DjangoAuth()} + +# +# Library resources +# + +import api.handlers.library_handlers as dh +library_resource = Resource(dh.LibraryHandler, **authdata) +document_resource = Resource(dh.DocumentHandler, **authdata) +document_text_resource = Resource(dh.DocumentTextHandler, **authdata) +document_dc_resource = Resource(dh.DocumentDublinCoreHandler, **authdata) + +# +# Toolbar resources +# +import api.handlers.toolbar_handlers as th +toolbar_buttons = Resource(th.ToolbarHandler, **authdata) +scriptlets = Resource(th.ScriptletsHandler, **authdata) + +__all__ = [ + 'library_resource', + 'document_resource', + 'document_text_resource', + 'document_dc_resource', + 'toolbar_buttons', + 'scriptlets' +] \ No newline at end of file diff --git a/apps/api/tests/__init__.py b/apps/api/tests/__init__.py index ca575fe7..2292e7dd 100644 --- a/apps/api/tests/__init__.py +++ b/apps/api/tests/__init__.py @@ -1,10 +1,3 @@ -""" -This file demonstrates two different styles of tests (one doctest and one -unittest). These will both pass when you run "manage.py test". - -Replace these with more appropriate tests for your application. -""" - from django.test import TestCase from django.test.client import Client from django.core.urlresolvers import reverse @@ -21,8 +14,6 @@ import tempfile REPO_TEMPLATES = join(dirname(__file__), 'data') - - def temprepo(name): from functools import wraps @@ -32,7 +23,7 @@ def temprepo(name): def decorated(self, *args, **kwargs): clean = False try: - temp = tempfile.mkdtemp("", "testdir_" ) + temp = tempfile.mkdtemp("-test", func.__name__) shutil.copytree(join(REPO_TEMPLATES, name), join(temp, 'repo'), False) settings.REPOSITORY_PATH = join(temp, 'repo') func(self, *args, **kwargs) @@ -43,7 +34,7 @@ def temprepo(name): print self.response print "<<<" - shutil.rmtree(temp, True) + # shutil.rmtree(temp, True) settings.REPOSITORY_PATH = '' return decorated @@ -57,27 +48,23 @@ class SimpleTest(TestCase): u = User.objects.create_user('admin', 'test@localhost', 'admin') u.save() - @temprepo('empty') + @temprepo('clean') def test_documents_get_anonymous(self): self.response = self.client.get( reverse("document_list_view") ) - self.assert_json_response({ - u'latest_rev': u'e56b2a7e06a97d7c3697fc4295974e0f20a66190', - u'documents': [], - u'cabinet': u'default', - }, exclude=['latest_shared_rev']) + self.assert_json_response({ + u'documents': [], + }) - @temprepo('empty') + @temprepo('clean') def test_documents_get_with_login(self): self.assertTrue(self.client.login(username='admin', password='admin')) self.response = self.client.get( reverse("document_list_view") ) - self.assert_json_response({ - u'latest_rev': u'e56b2a7e06a97d7c3697fc4295974e0f20a66190', - u'documents': [], - u'cabinet': u'default', + self.assert_json_response({ + u'documents': [], }) - @temprepo('empty') + @temprepo('clean') def test_documents_post(self): self.assertTrue(self.client.login(username='admin', password='admin')) @@ -85,8 +72,8 @@ class SimpleTest(TestCase): infile.write('0123456789') infile.flush() infile.seek(0) - - self.response = self.client.post( reverse("document_list_view"), + + self.response = self.client.post( reverse("document_list_view"), data = { 'bookname': 'testbook', 'ocr': infile, @@ -94,15 +81,15 @@ class SimpleTest(TestCase): }) infile.close() - - result = self.assert_json_response({ + + self.assert_json_response({ 'url': reverse('document_view', args=['testbook']), 'name': 'testbook', - 'size': 10, + # 'size': 10, # can't test revision number, 'cause it's random },) - @temprepo('empty') + @temprepo('clean') def test_document_creation(self): self.assertTrue(self.client.login(username='admin', password='admin')) @@ -117,73 +104,82 @@ class SimpleTest(TestCase): 'ocr': infile, 'generate_dc': False, }) - + r = self.assert_json_response({ 'url': reverse('document_view', args=['testbook']), - 'name': 'testbook', - 'size': 15, + 'name': 'testbook', # can't test revision number, 'cause it's random }) created_rev = r['revision'] self.response = self.client.get( \ - reverse("document_view", args=["testbook"])+'?autocabinet=true' ) + reverse("document_view", args=["testbook"]) ) result = self.assert_json_response({ u'latest_shared_rev': created_rev, - u'size': 15, + # u'size': 15, }) - @temprepo('testone') + @temprepo('simple') def test_document_meta_get_with_login(self): self.assertTrue(self.client.login(username='admin', password='admin')) self.response = self.client.get( reverse("document_list_view") ) self.assert_json_response({ - u'latest_rev': u'f94a263812dbe46a3a13d5209bb119988d0078d5', - u'documents': [{u'url': u'/api/documents/testfile', u'name': u'testfile'}], - u'cabinet': u'default', + # u'latest_rev': u'f94a263812dbe46a3a13d5209bb119988d0078d5', + u'documents': [{u'url': u'/api/documents/sample', u'name': u'sample'}, + {u'url': u'/api/documents/sample_pl', u'name': u'sample_pl'}], }) self.response = self.client.get( \ - reverse("document_view", args=['testfile'])+'?autocabinet=true' ) + reverse("document_view", args=['sample']) ) self.assert_json_response({ - u'latest_shared_rev': u'f94a263812dbe46a3a13d5209bb119988d0078d5', - u'text_url': reverse("doctext_view", args=[u'testfile']), - u'dc_url': reverse("docdc_view", args=[u'testfile']), - u'parts_url': reverse("docparts_view", args=[u'testfile']), - u'name': u'testfile', - u'size': 20, + #u'latest_shared_rev': u'f94a263812dbe46a3a13d5209bb119988d0078d5', + u'text_url': reverse("doctext_view", args=[u'sample']), + u'dc_url': reverse("docdc_view", args=[u'sample']), + # u'parts_url': reverse("docparts_view", args=[u'sample']), + u'name': u'sample', + # u'size': 20, }) - - - @temprepo('test2') - def test_document_text_with_login(self): - self.assertTrue(self.client.login(username='admin', password='admin')) - - self.response = self.client.get( \ - reverse("doctext_view", args=['testfile']) ) - - self.assertEqual(self.response.status_code, 200) - self.assertEqual(self.response.content, "Test file contents.\n") - @temprepo('test2') - def test_document_text_update(self): + @temprepo('simple') + def test_document_text_with_login(self): self.assertTrue(self.client.login(username='admin', password='admin')) - TEXT = u"Ala ma kota i psa" - - self.response = self.client.put( \ - reverse("doctext_view", args=['testfile']), {'contents': TEXT }) - self.assertEqual(self.response.status_code, 200) self.response = self.client.get( \ - reverse("doctext_view", args=['testfile']) ) + reverse("document_view", args=['sample']) ) + + resp = self.assert_json_response({ + #u'latest_shared_rev': u'f94a263812dbe46a3a13d5209bb119988d0078d5', + u'text_url': reverse("doctext_view", args=[u'sample']), + u'dc_url': reverse("docdc_view", args=[u'sample']), + # u'parts_url': reverse("docparts_view", args=[u'sample']), + u'name': u'sample', + # u'size': 20, + }) + + self.response = self.client.get(resp['text_url']) self.assertEqual(self.response.status_code, 200) - self.assertEqual(self.response.content, TEXT) + self.assertEqual(self.response.content, "Ala ma kota\n") +# +# +# @temprepo('simple') +# def test_document_text_update(self): +# self.assertTrue(self.client.login(username='admin', password='admin')) +# TEXT = u"Ala ma kota i psa" +# +# self.response = self.client.put( \ +# reverse("doctext_view", args=['testfile']), {'contents': TEXT }) +# self.assertEqual(self.response.status_code, 200) +# +# self.response = self.client.get( \ +# reverse("doctext_view", args=['testfile']) ) +# self.assertEqual(self.response.status_code, 200) +# self.assertEqual(self.response.content, TEXT) def assert_json_response(self, must_have={}, exclude=[]): self.assertEqual(self.response.status_code, 200) diff --git a/apps/api/tests/data/clean/$meta b/apps/api/tests/data/clean/$meta new file mode 100644 index 00000000..e69de29b diff --git a/apps/api/tests/data/clean/.hg/00changelog.i b/apps/api/tests/data/clean/.hg/00changelog.i new file mode 100644 index 00000000..d3a83110 Binary files /dev/null and b/apps/api/tests/data/clean/.hg/00changelog.i differ diff --git a/apps/api/tests/data/clean/.hg/dirstate b/apps/api/tests/data/clean/.hg/dirstate new file mode 100644 index 00000000..417cb6ba Binary files /dev/null and b/apps/api/tests/data/clean/.hg/dirstate differ diff --git a/apps/api/tests/data/clean/.hg/requires b/apps/api/tests/data/clean/.hg/requires new file mode 100644 index 00000000..5175383b --- /dev/null +++ b/apps/api/tests/data/clean/.hg/requires @@ -0,0 +1,3 @@ +revlogv1 +store +fncache diff --git a/apps/api/tests/data/clean/.hg/store/00changelog.i b/apps/api/tests/data/clean/.hg/store/00changelog.i new file mode 100644 index 00000000..671cfac1 Binary files /dev/null and b/apps/api/tests/data/clean/.hg/store/00changelog.i differ diff --git a/apps/api/tests/data/clean/.hg/store/00manifest.i b/apps/api/tests/data/clean/.hg/store/00manifest.i new file mode 100644 index 00000000..6f5dfeb7 Binary files /dev/null and b/apps/api/tests/data/clean/.hg/store/00manifest.i differ diff --git a/apps/api/tests/data/clean/.hg/store/data/$meta.i b/apps/api/tests/data/clean/.hg/store/data/$meta.i new file mode 100644 index 00000000..2431023a Binary files /dev/null and b/apps/api/tests/data/clean/.hg/store/data/$meta.i differ diff --git a/apps/api/tests/data/clean/.hg/store/data/.hgignore.i b/apps/api/tests/data/clean/.hg/store/data/.hgignore.i new file mode 100644 index 00000000..2431023a Binary files /dev/null and b/apps/api/tests/data/clean/.hg/store/data/.hgignore.i differ diff --git a/apps/api/tests/data/clean/.hg/store/data/.hgtags.i b/apps/api/tests/data/clean/.hg/store/data/.hgtags.i new file mode 100644 index 00000000..74da2382 Binary files /dev/null and b/apps/api/tests/data/clean/.hg/store/data/.hgtags.i differ diff --git a/apps/api/tests/data/clean/.hg/store/fncache b/apps/api/tests/data/clean/.hg/store/fncache new file mode 100644 index 00000000..3a7e3204 --- /dev/null +++ b/apps/api/tests/data/clean/.hg/store/fncache @@ -0,0 +1,3 @@ +data/$meta.i +data/.hgignore.i +data/.hgtags.i diff --git a/apps/api/tests/data/clean/.hg/store/undo b/apps/api/tests/data/clean/.hg/store/undo new file mode 100644 index 00000000..c86109fd Binary files /dev/null and b/apps/api/tests/data/clean/.hg/store/undo differ diff --git a/apps/api/tests/data/clean/.hg/undo.branch b/apps/api/tests/data/clean/.hg/undo.branch new file mode 100644 index 00000000..331d858c --- /dev/null +++ b/apps/api/tests/data/clean/.hg/undo.branch @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/apps/api/tests/data/clean/.hg/undo.dirstate b/apps/api/tests/data/clean/.hg/undo.dirstate new file mode 100644 index 00000000..f2fcbe22 Binary files /dev/null and b/apps/api/tests/data/clean/.hg/undo.dirstate differ diff --git a/apps/api/tests/data/clean/.hgignore b/apps/api/tests/data/clean/.hgignore new file mode 100644 index 00000000..e69de29b diff --git a/apps/api/tests/data/clean/.hgtags b/apps/api/tests/data/clean/.hgtags new file mode 100644 index 00000000..0b061138 --- /dev/null +++ b/apps/api/tests/data/clean/.hgtags @@ -0,0 +1 @@ +d5e516ebd357bca24c6afb737e97db3b3d4f111a $branchbase diff --git a/apps/api/tests/data/empty/.hg/00changelog.i b/apps/api/tests/data/empty/.hg/00changelog.i deleted file mode 100644 index d3a83110..00000000 Binary files a/apps/api/tests/data/empty/.hg/00changelog.i and /dev/null differ diff --git a/apps/api/tests/data/empty/.hg/branchheads.cache b/apps/api/tests/data/empty/.hg/branchheads.cache deleted file mode 100644 index 022ec463..00000000 --- a/apps/api/tests/data/empty/.hg/branchheads.cache +++ /dev/null @@ -1,2 +0,0 @@ -e56b2a7e06a97d7c3697fc4295974e0f20a66190 0 -e56b2a7e06a97d7c3697fc4295974e0f20a66190 default diff --git a/apps/api/tests/data/empty/.hg/dirstate b/apps/api/tests/data/empty/.hg/dirstate deleted file mode 100644 index 3996f893..00000000 Binary files a/apps/api/tests/data/empty/.hg/dirstate and /dev/null differ diff --git a/apps/api/tests/data/empty/.hg/requires b/apps/api/tests/data/empty/.hg/requires deleted file mode 100644 index 5175383b..00000000 --- a/apps/api/tests/data/empty/.hg/requires +++ /dev/null @@ -1,3 +0,0 @@ -revlogv1 -store -fncache diff --git a/apps/api/tests/data/empty/.hg/store/00changelog.i b/apps/api/tests/data/empty/.hg/store/00changelog.i deleted file mode 100644 index f6361419..00000000 Binary files a/apps/api/tests/data/empty/.hg/store/00changelog.i and /dev/null differ diff --git a/apps/api/tests/data/empty/.hg/store/00manifest.i b/apps/api/tests/data/empty/.hg/store/00manifest.i deleted file mode 100644 index 267c71ad..00000000 Binary files a/apps/api/tests/data/empty/.hg/store/00manifest.i and /dev/null differ diff --git a/apps/api/tests/data/empty/.hg/store/data/.library.i b/apps/api/tests/data/empty/.hg/store/data/.library.i deleted file mode 100644 index 2431023a..00000000 Binary files a/apps/api/tests/data/empty/.hg/store/data/.library.i and /dev/null differ diff --git a/apps/api/tests/data/empty/.hg/store/fncache b/apps/api/tests/data/empty/.hg/store/fncache deleted file mode 100644 index d4a556ec..00000000 --- a/apps/api/tests/data/empty/.hg/store/fncache +++ /dev/null @@ -1 +0,0 @@ -data/.library.i diff --git a/apps/api/tests/data/empty/.hg/store/undo b/apps/api/tests/data/empty/.hg/store/undo deleted file mode 100644 index 33dec7fc..00000000 Binary files a/apps/api/tests/data/empty/.hg/store/undo and /dev/null differ diff --git a/apps/api/tests/data/empty/.hg/undo.branch b/apps/api/tests/data/empty/.hg/undo.branch deleted file mode 100644 index 331d858c..00000000 --- a/apps/api/tests/data/empty/.hg/undo.branch +++ /dev/null @@ -1 +0,0 @@ -default \ No newline at end of file diff --git a/apps/api/tests/data/empty/.hg/undo.dirstate b/apps/api/tests/data/empty/.hg/undo.dirstate deleted file mode 100644 index ebf59c66..00000000 Binary files a/apps/api/tests/data/empty/.hg/undo.dirstate and /dev/null differ diff --git a/apps/api/tests/data/empty/.library b/apps/api/tests/data/empty/.library deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/tests/data/simple/$meta b/apps/api/tests/data/simple/$meta new file mode 100644 index 00000000..e69de29b diff --git a/apps/api/tests/data/simple/.hg/00changelog.i b/apps/api/tests/data/simple/.hg/00changelog.i new file mode 100644 index 00000000..d3a83110 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/00changelog.i differ diff --git a/apps/api/tests/data/simple/.hg/branch b/apps/api/tests/data/simple/.hg/branch new file mode 100644 index 00000000..4ad96d51 --- /dev/null +++ b/apps/api/tests/data/simple/.hg/branch @@ -0,0 +1 @@ +default diff --git a/apps/api/tests/data/simple/.hg/branchheads.cache b/apps/api/tests/data/simple/.hg/branchheads.cache new file mode 100644 index 00000000..713758bd --- /dev/null +++ b/apps/api/tests/data/simple/.hg/branchheads.cache @@ -0,0 +1,4 @@ +65046aefea667e38c7a01d7ca62412d7e81f68bc 2 +d5e516ebd357bca24c6afb737e97db3b3d4f111a default +00e4aa6f4f53f31020e4d3e0579cb3c0fa0bd056 $doc:sample +65046aefea667e38c7a01d7ca62412d7e81f68bc $doc:sample_pl diff --git a/apps/api/tests/data/simple/.hg/dirstate b/apps/api/tests/data/simple/.hg/dirstate new file mode 100644 index 00000000..46fefcaa Binary files /dev/null and b/apps/api/tests/data/simple/.hg/dirstate differ diff --git a/apps/api/tests/data/simple/.hg/requires b/apps/api/tests/data/simple/.hg/requires new file mode 100644 index 00000000..5175383b --- /dev/null +++ b/apps/api/tests/data/simple/.hg/requires @@ -0,0 +1,3 @@ +revlogv1 +store +fncache diff --git a/apps/api/tests/data/simple/.hg/store/00changelog.i b/apps/api/tests/data/simple/.hg/store/00changelog.i new file mode 100644 index 00000000..f2451544 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/00changelog.i differ diff --git a/apps/api/tests/data/simple/.hg/store/00manifest.i b/apps/api/tests/data/simple/.hg/store/00manifest.i new file mode 100644 index 00000000..fd65f619 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/00manifest.i differ diff --git a/apps/api/tests/data/simple/.hg/store/data/$meta.i b/apps/api/tests/data/simple/.hg/store/data/$meta.i new file mode 100644 index 00000000..2431023a Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/data/$meta.i differ diff --git a/apps/api/tests/data/simple/.hg/store/data/.hgignore.i b/apps/api/tests/data/simple/.hg/store/data/.hgignore.i new file mode 100644 index 00000000..2431023a Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/data/.hgignore.i differ diff --git a/apps/api/tests/data/simple/.hg/store/data/.hgtags.i b/apps/api/tests/data/simple/.hg/store/data/.hgtags.i new file mode 100644 index 00000000..6c3fcf23 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/data/.hgtags.i differ diff --git a/apps/api/tests/data/simple/.hg/store/data/sample.parts.i b/apps/api/tests/data/simple/.hg/store/data/sample.parts.i new file mode 100644 index 00000000..a6bdf46f Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/data/sample.parts.i differ diff --git a/apps/api/tests/data/simple/.hg/store/data/sample.xml.i b/apps/api/tests/data/simple/.hg/store/data/sample.xml.i new file mode 100644 index 00000000..b115f4d6 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/data/sample.xml.i differ diff --git a/apps/api/tests/data/simple/.hg/store/data/sample__pl.xml.i b/apps/api/tests/data/simple/.hg/store/data/sample__pl.xml.i new file mode 100644 index 00000000..920c5742 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/data/sample__pl.xml.i differ diff --git a/apps/api/tests/data/simple/.hg/store/fncache b/apps/api/tests/data/simple/.hg/store/fncache new file mode 100644 index 00000000..c228181f --- /dev/null +++ b/apps/api/tests/data/simple/.hg/store/fncache @@ -0,0 +1,6 @@ +data/$meta.i +data/.hgignore.i +data/sample.parts.i +data/sample.xml.i +data/sample_pl.xml.i +data/.hgtags.i diff --git a/apps/api/tests/data/simple/.hg/store/undo b/apps/api/tests/data/simple/.hg/store/undo new file mode 100644 index 00000000..3ad8b327 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/store/undo differ diff --git a/apps/api/tests/data/simple/.hg/strip-backup/26716f931d95-backup b/apps/api/tests/data/simple/.hg/strip-backup/26716f931d95-backup new file mode 100644 index 00000000..424de921 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/strip-backup/26716f931d95-backup differ diff --git a/apps/api/tests/data/simple/.hg/undo.branch b/apps/api/tests/data/simple/.hg/undo.branch new file mode 100644 index 00000000..331d858c --- /dev/null +++ b/apps/api/tests/data/simple/.hg/undo.branch @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/apps/api/tests/data/simple/.hg/undo.dirstate b/apps/api/tests/data/simple/.hg/undo.dirstate new file mode 100644 index 00000000..310d9416 Binary files /dev/null and b/apps/api/tests/data/simple/.hg/undo.dirstate differ diff --git a/apps/api/tests/data/simple/.hgignore b/apps/api/tests/data/simple/.hgignore new file mode 100644 index 00000000..e69de29b diff --git a/apps/api/tests/data/simple/.hgtags b/apps/api/tests/data/simple/.hgtags new file mode 100644 index 00000000..0b061138 --- /dev/null +++ b/apps/api/tests/data/simple/.hgtags @@ -0,0 +1 @@ +d5e516ebd357bca24c6afb737e97db3b3d4f111a $branchbase diff --git a/apps/api/tests/data/test2/.hg/00changelog.i b/apps/api/tests/data/test2/.hg/00changelog.i deleted file mode 100644 index d3a83110..00000000 Binary files a/apps/api/tests/data/test2/.hg/00changelog.i and /dev/null differ diff --git a/apps/api/tests/data/test2/.hg/branch b/apps/api/tests/data/test2/.hg/branch deleted file mode 100644 index 4ad96d51..00000000 --- a/apps/api/tests/data/test2/.hg/branch +++ /dev/null @@ -1 +0,0 @@ -default diff --git a/apps/api/tests/data/test2/.hg/branchheads.cache b/apps/api/tests/data/test2/.hg/branchheads.cache deleted file mode 100644 index 650c9b01..00000000 --- a/apps/api/tests/data/test2/.hg/branchheads.cache +++ /dev/null @@ -1,3 +0,0 @@ -93618f13c33aab5a3d049e0860cd3d21a457cd7d 2 -f94a263812dbe46a3a13d5209bb119988d0078d5 default -93618f13c33aab5a3d049e0860cd3d21a457cd7d personal_admin_file_testfile diff --git a/apps/api/tests/data/test2/.hg/dirstate b/apps/api/tests/data/test2/.hg/dirstate deleted file mode 100644 index cc53c86d..00000000 Binary files a/apps/api/tests/data/test2/.hg/dirstate and /dev/null differ diff --git a/apps/api/tests/data/test2/.hg/requires b/apps/api/tests/data/test2/.hg/requires deleted file mode 100644 index 5175383b..00000000 --- a/apps/api/tests/data/test2/.hg/requires +++ /dev/null @@ -1,3 +0,0 @@ -revlogv1 -store -fncache diff --git a/apps/api/tests/data/test2/.hg/store/00changelog.i b/apps/api/tests/data/test2/.hg/store/00changelog.i deleted file mode 100644 index 4d3d86a3..00000000 Binary files a/apps/api/tests/data/test2/.hg/store/00changelog.i and /dev/null differ diff --git a/apps/api/tests/data/test2/.hg/store/00manifest.i b/apps/api/tests/data/test2/.hg/store/00manifest.i deleted file mode 100644 index de399682..00000000 Binary files a/apps/api/tests/data/test2/.hg/store/00manifest.i and /dev/null differ diff --git a/apps/api/tests/data/test2/.hg/store/data/.library.i b/apps/api/tests/data/test2/.hg/store/data/.library.i deleted file mode 100644 index 2431023a..00000000 Binary files a/apps/api/tests/data/test2/.hg/store/data/.library.i and /dev/null differ diff --git a/apps/api/tests/data/test2/.hg/store/data/pub__testfile.xml.i b/apps/api/tests/data/test2/.hg/store/data/pub__testfile.xml.i deleted file mode 100644 index ca417810..00000000 Binary files a/apps/api/tests/data/test2/.hg/store/data/pub__testfile.xml.i and /dev/null differ diff --git a/apps/api/tests/data/test2/.hg/store/fncache b/apps/api/tests/data/test2/.hg/store/fncache deleted file mode 100644 index d55636d5..00000000 --- a/apps/api/tests/data/test2/.hg/store/fncache +++ /dev/null @@ -1,2 +0,0 @@ -data/.library.i -data/pub_testfile.xml.i diff --git a/apps/api/tests/data/test2/.hg/store/undo b/apps/api/tests/data/test2/.hg/store/undo deleted file mode 100644 index 33e82075..00000000 Binary files a/apps/api/tests/data/test2/.hg/store/undo and /dev/null differ diff --git a/apps/api/tests/data/test2/.hg/undo.branch b/apps/api/tests/data/test2/.hg/undo.branch deleted file mode 100644 index 998eac45..00000000 --- a/apps/api/tests/data/test2/.hg/undo.branch +++ /dev/null @@ -1 +0,0 @@ -personal_admin_file_testfile \ No newline at end of file diff --git a/apps/api/tests/data/test2/.hg/undo.dirstate b/apps/api/tests/data/test2/.hg/undo.dirstate deleted file mode 100644 index 431d58a8..00000000 Binary files a/apps/api/tests/data/test2/.hg/undo.dirstate and /dev/null differ diff --git a/apps/api/tests/data/test2/.library b/apps/api/tests/data/test2/.library deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/tests/data/test2/pub_testfile.xml b/apps/api/tests/data/test2/pub_testfile.xml deleted file mode 100644 index 57873f23..00000000 --- a/apps/api/tests/data/test2/pub_testfile.xml +++ /dev/null @@ -1 +0,0 @@ -Test file contents. diff --git a/apps/api/tests/data/testone/.hg/00changelog.i b/apps/api/tests/data/testone/.hg/00changelog.i deleted file mode 100644 index d3a83110..00000000 Binary files a/apps/api/tests/data/testone/.hg/00changelog.i and /dev/null differ diff --git a/apps/api/tests/data/testone/.hg/branchheads.cache b/apps/api/tests/data/testone/.hg/branchheads.cache deleted file mode 100644 index 6087023a..00000000 --- a/apps/api/tests/data/testone/.hg/branchheads.cache +++ /dev/null @@ -1,2 +0,0 @@ -f94a263812dbe46a3a13d5209bb119988d0078d5 1 -f94a263812dbe46a3a13d5209bb119988d0078d5 default diff --git a/apps/api/tests/data/testone/.hg/dirstate b/apps/api/tests/data/testone/.hg/dirstate deleted file mode 100644 index e146f184..00000000 Binary files a/apps/api/tests/data/testone/.hg/dirstate and /dev/null differ diff --git a/apps/api/tests/data/testone/.hg/requires b/apps/api/tests/data/testone/.hg/requires deleted file mode 100644 index 5175383b..00000000 --- a/apps/api/tests/data/testone/.hg/requires +++ /dev/null @@ -1,3 +0,0 @@ -revlogv1 -store -fncache diff --git a/apps/api/tests/data/testone/.hg/store/00changelog.i b/apps/api/tests/data/testone/.hg/store/00changelog.i deleted file mode 100644 index c466ccb5..00000000 Binary files a/apps/api/tests/data/testone/.hg/store/00changelog.i and /dev/null differ diff --git a/apps/api/tests/data/testone/.hg/store/00manifest.i b/apps/api/tests/data/testone/.hg/store/00manifest.i deleted file mode 100644 index 0239a8f7..00000000 Binary files a/apps/api/tests/data/testone/.hg/store/00manifest.i and /dev/null differ diff --git a/apps/api/tests/data/testone/.hg/store/data/.library.i b/apps/api/tests/data/testone/.hg/store/data/.library.i deleted file mode 100644 index 2431023a..00000000 Binary files a/apps/api/tests/data/testone/.hg/store/data/.library.i and /dev/null differ diff --git a/apps/api/tests/data/testone/.hg/store/data/pub__testfile.xml.i b/apps/api/tests/data/testone/.hg/store/data/pub__testfile.xml.i deleted file mode 100644 index ca417810..00000000 Binary files a/apps/api/tests/data/testone/.hg/store/data/pub__testfile.xml.i and /dev/null differ diff --git a/apps/api/tests/data/testone/.hg/store/fncache b/apps/api/tests/data/testone/.hg/store/fncache deleted file mode 100644 index d55636d5..00000000 --- a/apps/api/tests/data/testone/.hg/store/fncache +++ /dev/null @@ -1,2 +0,0 @@ -data/.library.i -data/pub_testfile.xml.i diff --git a/apps/api/tests/data/testone/.hg/store/undo b/apps/api/tests/data/testone/.hg/store/undo deleted file mode 100644 index 0dbe30a4..00000000 Binary files a/apps/api/tests/data/testone/.hg/store/undo and /dev/null differ diff --git a/apps/api/tests/data/testone/.hg/undo.branch b/apps/api/tests/data/testone/.hg/undo.branch deleted file mode 100644 index 331d858c..00000000 --- a/apps/api/tests/data/testone/.hg/undo.branch +++ /dev/null @@ -1 +0,0 @@ -default \ No newline at end of file diff --git a/apps/api/tests/data/testone/.hg/undo.dirstate b/apps/api/tests/data/testone/.hg/undo.dirstate deleted file mode 100644 index a2fe6838..00000000 Binary files a/apps/api/tests/data/testone/.hg/undo.dirstate and /dev/null differ diff --git a/apps/api/tests/data/testone/.library b/apps/api/tests/data/testone/.library deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/api/tests/data/testone/pub_testfile.xml b/apps/api/tests/data/testone/pub_testfile.xml deleted file mode 100644 index 57873f23..00000000 --- a/apps/api/tests/data/testone/pub_testfile.xml +++ /dev/null @@ -1 +0,0 @@ -Test file contents. diff --git a/apps/api/urls.py b/apps/api/urls.py index 913acb7c..de09605d 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -2,24 +2,21 @@ __author__="lreqc" __date__ ="$2009-09-17 16:16:54$" from django.conf.urls.defaults import * -from piston.resource import Resource -from api.handlers import * -from api.utils import TextEmitter, DjangoAuth - -authdata = {'authentication': DjangoAuth()} +from api.resources import * FORMAT_EXT = r"\.(?Pxml|json|yaml)$" -library_resource = Resource(LibraryHandler, **authdata) -document_resource = Resource(DocumentHandler, **authdata) -document_text_resource = Resource(DocumentTextHandler, **authdata) -document_dc_resource = Resource(DocumentDublinCoreHandler, **authdata) - urlpatterns = patterns('', # url(r'^hello$', hello_resource, {'emitter_format': 'json'}), # url(r'^hello\.(?P.+)$', hello_resource), + # Toolbar + url(r'^toolbar/buttons$', toolbar_buttons, {'emitter_format': 'json'}), + + # Toolbar + url(r'^toolbar/scriptlets$', scriptlets, {'emitter_format': 'json'}), + # Documents url(r'^documents$', library_resource, {'emitter_format': 'json'}, name="document_list_view"), diff --git a/apps/toolbar/models.py b/apps/toolbar/models.py index d1f4952c..04146223 100644 --- a/apps/toolbar/models.py +++ b/apps/toolbar/models.py @@ -14,6 +14,26 @@ class ButtonGroup(models.Model): def __unicode__(self): return self.name + +#class ButtonGroupManager(models.Manager): +# +# def with_buttons(self): +# from django.db import connection +# cursor = connection.cursor() +# cursor.execute(""" +# SELECT g.name, g.slug, CONCAT(b.slug), +# FROM toolbar_buttongroup as g LEFT JOIN toolbar_button as b +# +# WHERE p.id = r.poll_id +# GROUP BY 1, 2, 3 +# ORDER BY 3 DESC""") +# result_list = [] +# for row in cursor.fetchall(): +# p = self.model(id=row[0], question=row[1], poll_date=row[2]) +# p.num_responses = row[3] +# result_list.append(p) +# return result_list + class Button(models.Model): label = models.CharField(max_length=32) slug = models.SlugField(unique=True) #unused diff --git a/apps/wysiwyg/__init__.py b/apps/wysiwyg/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/wysiwyg/models.py b/apps/wysiwyg/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/apps/wysiwyg/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/apps/wysiwyg/tests.py b/apps/wysiwyg/tests.py new file mode 100644 index 00000000..2247054b --- /dev/null +++ b/apps/wysiwyg/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/apps/wysiwyg/urls.py b/apps/wysiwyg/urls.py new file mode 100644 index 00000000..ca19ea97 --- /dev/null +++ b/apps/wysiwyg/urls.py @@ -0,0 +1,12 @@ +# -*- encoding: utf-8 -*- + +__author__= "Łukasz Rekucki" +__date__ = "$2009-09-23 15:57:38$" +__doc__ = "Module documentation." + +from django.conf.urls.defaults import * + + +urlpatterns = patterns('', + (r'^$', 'wysiwyg.views.index'), +) diff --git a/apps/wysiwyg/views.py b/apps/wysiwyg/views.py new file mode 100644 index 00000000..e588c49e --- /dev/null +++ b/apps/wysiwyg/views.py @@ -0,0 +1,5 @@ +# Create your views here. +from django.views.generic.simple import direct_to_template + +def index(request): + return direct_to_template(request, 'wysiwyg.html', extra_context={}) \ No newline at end of file diff --git a/lib/wlrepo/__init__.py b/lib/wlrepo/__init__.py index 1c1335e8..cbc0c2c6 100644 --- a/lib/wlrepo/__init__.py +++ b/lib/wlrepo/__init__.py @@ -1,7 +1,6 @@ # -*- encoding: utf-8 -*- __author__="Łukasz Rekucki" __date__ ="$2009-09-18 10:49:24$" - __doc__ = """Main module for the Repository Abstraction Layer""" class Library(object): @@ -9,157 +8,107 @@ class Library(object): def __init__(self, create=False): """Open an existing library, or create a new one. By default, fails if the library doesn't exist.""" - self.create = create - - def main_cabinet(self): - """Return the "main" cabinet of the library.""" - pass + self.create = create - def cabinets(self): - """List all cabinets in the library.""" + def documents(self): + """List all documents in the library.""" pass - def cabinet(self, document, user, create=False): - """Open a cabinet belonging to the _user_ for a given _document_. - If the _document_ is actually a sub-document, it's parent's cabinet is - opened istead. - - If the cabinet doesn't exists and create is False (the default), a - CabinetNotFound exception is raised. - - If create is True, a new cabinet is created if it doesn't exist yet.""" + def document_for_rev(self, rev): + """Retrieve a document in the specified revision.""" pass - - def document(self, docid, user, part=None, shelve=None): + def document(self, docid, user=None): + """Retrieve a document from a library.""" pass + def get_revision(self, revid): + """Retrieve a handle to a specified revision.""" + return None -class Cabinet(object): + def document_create(self, docid): + """Create a new document. The document will have it's own branch.""" + + +class Document(object): + """A class representing a document package boundled with a revision.""" - def __init__(self, library, name=None, doc=None, user=None): + def __init__(self, library, revision): + """_library_ should be an instance of a Library.""" self._library = library - if name: - self._name = name - self._maindoc = '' - self._user = self._document = None - elif doc and user: - self._user = user - self._document = doc - self._name = user + ':' + doc - self._maindoc = doc + if isinstance(revision, Revision): + self._revision = revision else: - raise ValueError("You must provide either name or doc and user.") + self._revision = library.get_revision(revision) - print "new cab:", self._name, self._user, self._document - - @property - def username(self): - return self._user - def __str__(self): - return "Cabinet(%s)" % self._name - - def parts(self): - """Lists all parts in this cabinet.""" + def take(self, user): + """Make a user copy of the document. This is persistant.""" pass - - def retrieve(self, part='xml', shelve=None): - """Retrieve a document from a given shelve in the cabinet. If no - part is given, the main document is retrieved. If no shelve is given, - the top-most shelve is used. - If parts is a list, all the given parts are retrieved atomicly. Use None - as the name for the main document""" + def giveback(self): + """Informs the library, that the user no longer needs this document. + Should be called on the user version of document. If not, it doesn nothing.""" + + def data(self, entry): + """Returns the specified entry as a file-like object.""" pass - def create(self, name, initial_data=''): - """Create a new part in the cabinet with the given name.""" - pass - - @property - def maindoc_name(self): - return self._maindoc - @property def library(self): return self._library @property - def name(self): - return self._name - - def shelf(self, selector=None): - pass - -class Document(object): - def __init__(self, cabinet, name): - self._cabinet = cabinet - self._name = name - - def read(self): - pass - - def write(self, data): - pass - - @property - def cabinet(self): - return self._cabinet + def revision(self): + return self._revision @property - def library(self): - return self._cabinet.library + def id(self): + return self._revision.document_name @property - def name(self): - return self._name - - def shelf(self): - return self._cabinet.shelf() - - @property - def size(self): - raise NotImplemented() - - @property - def parts(self): - raise NotImplemented() + def owner(self): + return self._revision.user_name def parentof(self, other): - return self.shelf().parentof(other.shelf()) + return self._revision.parentof(other._revision) def ancestorof(self, other): - return self.shelf().ancestorof(other.shelf()) + return self._revision.ancestorof(other._revision) -class Shelf(object): +class Revision(object): def __init__(self, lib): self._library = lib - def parentof(self, other): return False def ancestorof(self, other): return False - + + @property + def document_name(self): + raise ValueError() + + @property + def user_name(self): + raise ValueError() + # # Exception classes # -class LibraryException(Exception): - +class LibraryException(Exception): def __init__(self, msg, cause=None): Exception.__init__(self, msg) self.cause = cause -class CabinetNotFound(LibraryException): - def __init__(self, cabname): - LibraryException.__init__(self, "Cabinet '%s' not found." % cabname) +class RevisionNotFound(LibraryException): + def __init__(self, rev): + LibraryException.__init__(self, "Revision %r not found." % rev) pass - # import backends to local namespace -from backend_mercurial import MercurialLibrary \ No newline at end of file +from mercurial_backend.library import MercurialLibrary \ No newline at end of file diff --git a/lib/wlrepo/backend_mercurial.py b/lib/wlrepo/backend_mercurial.py index a082b8b4..35e4442e 100644 --- a/lib/wlrepo/backend_mercurial.py +++ b/lib/wlrepo/backend_mercurial.py @@ -1,565 +1,565 @@ -# -*- 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 +## -*- 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 new file mode 100644 index 00000000..10a4cf8a --- /dev/null +++ b/lib/wlrepo/mercurial_backend/__init__.py @@ -0,0 +1,84 @@ +# -*- encoding: utf-8 -*- + +__author__= "Łukasz Rekucki" +__date__ = "$2009-09-25 09:20:22$" +__doc__ = "Module documentation." + +import wlrepo + + + +class MercurialRevision(wlrepo.Revision): + + def __init__(self, lib, changectx): + super(MercurialRevision, self).__init__(lib) + self._changectx = changectx + + branchname = self._changectx.branch() + if branchname.startswith("$doc:"): + self._docname = branchname[5:] + self._username = None + elif branchname.startswith("$user:"): + idx = branchname.find("$doc:") + if(idx < 0): + raise ValueError("Revision %s is not a valid document revision." % changectx.hex()); + self._username = branchname[0:idx] + self._docname = branchname[idx+5:] + else: + raise ValueError("Revision %s is not a valid document revision." % changectx.hex()); + + @property + def document_name(self): + return self._docname + + @property + def user_name(self): + return self._username + + def hgrev(self): + return self._changectx.node() + + def hgcontext(self): + return self._changectx + + def hgbranch(self): + return self._changectx.branch() + + def __unicode__(self): + return u"%s" % self._changectx.hex() + + def __repr__(self): + return "%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) + 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() + + +from wlrepo.mercurial_backend.library import MercurialLibrary + + diff --git a/lib/wlrepo/mercurial_backend/document.py b/lib/wlrepo/mercurial_backend/document.py new file mode 100644 index 00000000..bced6741 --- /dev/null +++ b/lib/wlrepo/mercurial_backend/document.py @@ -0,0 +1,173 @@ +# -*- encoding: utf-8 -*- + +__author__ = "Łukasz Rekucki" +__date__ = "$2009-09-25 09:35:06$" +__doc__ = "Module documentation." + +import wlrepo + +class MercurialDocument(wlrepo.Document): + + def data(self, entry): + path = self._revision._docname + '.' + entry + return self._library._filectx(path, \ + self._revision.hgrev()).data() + + def quickwrite(self, entry, data, msg, user=None): + user = user or self.owner + if user is None: + raise ValueError("Can't determine user.") + + def write(l, r): + f = l._fileopen(r(entry), "w+") + f.write(data) + f.close() + l._fileadd(r(entry)) + + return self.invoke_and_commit(write, lambda d: (msg, user)) + + def invoke_and_commit(self, ops, before_commit): + lock = self._library._lock() + try: + self._library._checkout(self._revision.hgrev()) + + def entry_path(entry): + return self.id + '.' + entry + + ops(self._library, entry_path) + message, user = before_commit(self) + self._library._commit(message, user) + + return self._library.document(docid=self.id, user=self.owner) + finally: + lock.release() + + # def commit(self, message, user): + # """Make a new commit.""" + # self.invoke_and_commit(message, user, lambda *a: True) + + def ismain(self): + return self._revision.user_name() is None + + def shared(self): + if self.ismain(): + return self + return self._library.document(docid=self._revision.document_name()) + + + def take(self, user): + fullid = self._library.fulldocid(self.id, user) + + def take_action(library, resolve): + # branch from latest + library._create_branch(fullid, parent=self._revision) + + if not self._library.has_revision(fullid): + self.invoke_and_commit(take_action, \ + lambda d: ("$AUTO$ File checkout.", user) ) + + return self._library.document_for_rev(fullid) + + def update(self, user): + """Update parts of the document.""" + lock = self.library._lock() + try: + if self.ismain(): + # main revision of the document + return True + + if self._revision.has_children(): + # can't update non-latest revision + return False + + sv = self.shared() + + if not sv.ancestorof(self) and not self.parentof(sv): + self._revision.merge_with(sv._revision, user=user) + + return True + finally: + lock.release() + + def share(self, message): + lock = self.library._lock() + try: + if self.ismain(): + return True # always shared + + user = self._revision.user_name() + + main = self.shared()._revision + local = self._revision + + 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 __str__(self): + return u"Document(%s:%s)" % (self.name, self.owner) + + 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 new file mode 100644 index 00000000..219a01df --- /dev/null +++ b/lib/wlrepo/mercurial_backend/library.py @@ -0,0 +1,250 @@ +# -*- encoding: utf-8 -*- + +__author__= "Łukasz Rekucki" +__date__ = "$2009-09-25 09:33:02$" +__doc__ = "Module documentation." + +import mercurial +from mercurial import localrepo as hglrepo +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 + +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)) + +class MercurialLibrary(wlrepo.Library): + """Mercurial implementation of the Library API""" + + def __init__(self, path, **kwargs): + super(wlrepo.Library, self).__init__( ** kwargs) + + self._revcache = {} + self._doccache = {} + + 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)) + + 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) + + + def documents(self): + return [ key[5:] for key in \ + self._hgrepo.branchmap() if key.startswith("$doc:") ] + + @property + def ospath(self): + return self._ospath + + def document_for_rev(self, revision): + if revision is None: + raise ValueError("Revision can't be None.") + + if not isinstance(revision, MercurialRevision): + rev = self.get_revision(revision) + else: + rev = revision + + if not self._doccache.has_key(str(rev)): + self._doccache[str(rev)] = MercurialDocument(self, rev) + + # every revision is a document + return self._doccache[str(rev)] + + def document(self, docid, user=None): + return self.document_for_rev(self.fulldocid(docid, user)) + + def get_revision(self, revid): + ctx = self._changectx(revid) + + if ctx is None: + raise RevisionNotFound(revid) + + if self._revcache.has_key(ctx): + return self._revcache[ctx] + + return MercurialRevision(self, ctx) + + def fulldocid(self, docid, user=None): + fulldocid = '' + if user is not None: + fulldocid += '$user:' + user + fulldocid += '$doc:' + docid + return fulldocid + + + def has_revision(self, revid): + try: + self._hgrepo[revid] + return True + except error.RepoError: + return False + + def document_create(self, docid): + # check if it already exists + fullid = self.fulldocid(docid) + + if self.has_revision(fullid): + raise LibraryException("Document already exists!"); + + # doesn't exist + self._create_branch(fullid) + return self.document_for_rev(fullid) + + # + # 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 _fileopen(self, path, mode): + return self._hgrepo.wopener(path, mode) + + def _filectx(self, fileid, revid): + return self._hgrepo.filectx(fileid, changeid=revid) + + 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: + parentrev = self._hgrepo['$branchbase'].node() + else: + parentrev = parent.hgrev() + + self._checkout(parentrev) + self._hgrepo.dirstate.setbranch(name) + + if before_commit: before_commit(self) + + self._commit("$ADMN$ Initial commit for branch '%s'." % name, user='$library') + + 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 + + # + # Utils + # + + @staticmethod + def _sanitize_string(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + + if ' ' in s: + raise ValueError('Whitespace is forbidden!') + + return s \ No newline at end of file diff --git a/lib/wlrepo/tests/data/clean/$meta b/lib/wlrepo/tests/data/clean/$meta new file mode 100644 index 00000000..e69de29b diff --git a/lib/wlrepo/tests/data/clean/.hg/dirstate b/lib/wlrepo/tests/data/clean/.hg/dirstate index b0aa1e64..20ad845e 100644 Binary files a/lib/wlrepo/tests/data/clean/.hg/dirstate and b/lib/wlrepo/tests/data/clean/.hg/dirstate differ diff --git a/lib/wlrepo/tests/data/clean/.hg/store/00changelog.i b/lib/wlrepo/tests/data/clean/.hg/store/00changelog.i index 45c1c461..b6c919d5 100644 Binary files a/lib/wlrepo/tests/data/clean/.hg/store/00changelog.i and b/lib/wlrepo/tests/data/clean/.hg/store/00changelog.i differ diff --git a/lib/wlrepo/tests/data/clean/.hg/store/00manifest.i b/lib/wlrepo/tests/data/clean/.hg/store/00manifest.i index ed99f61a..fb3412d2 100644 Binary files a/lib/wlrepo/tests/data/clean/.hg/store/00manifest.i and b/lib/wlrepo/tests/data/clean/.hg/store/00manifest.i differ diff --git a/lib/wlrepo/tests/data/clean/.hg/store/data/$meta.i b/lib/wlrepo/tests/data/clean/.hg/store/data/$meta.i new file mode 100644 index 00000000..2431023a Binary files /dev/null and b/lib/wlrepo/tests/data/clean/.hg/store/data/$meta.i differ diff --git a/lib/wlrepo/tests/data/clean/.hg/store/data/ignored__file.i b/lib/wlrepo/tests/data/clean/.hg/store/data/ignored__file.i deleted file mode 100644 index a6bdf46f..00000000 Binary files a/lib/wlrepo/tests/data/clean/.hg/store/data/ignored__file.i and /dev/null differ diff --git a/lib/wlrepo/tests/data/clean/.hg/store/data/pub__valid__file.xml.i b/lib/wlrepo/tests/data/clean/.hg/store/data/pub__valid__file.xml.i deleted file mode 100644 index a6bdf46f..00000000 Binary files a/lib/wlrepo/tests/data/clean/.hg/store/data/pub__valid__file.xml.i and /dev/null differ diff --git a/lib/wlrepo/tests/data/clean/.hg/store/fncache b/lib/wlrepo/tests/data/clean/.hg/store/fncache index a1607dd1..b996e345 100644 --- a/lib/wlrepo/tests/data/clean/.hg/store/fncache +++ b/lib/wlrepo/tests/data/clean/.hg/store/fncache @@ -1,3 +1,2 @@ +data/$meta.i data/.hgignore.i -data/ignored_file.i -data/pub_valid_file.xml.i diff --git a/lib/wlrepo/tests/data/clean/.hg/store/undo b/lib/wlrepo/tests/data/clean/.hg/store/undo index 90d4cb1d..d9a46ca8 100644 Binary files a/lib/wlrepo/tests/data/clean/.hg/store/undo and b/lib/wlrepo/tests/data/clean/.hg/store/undo differ diff --git a/lib/wlrepo/tests/data/clean/.hg/undo.dirstate b/lib/wlrepo/tests/data/clean/.hg/undo.dirstate index 1d0af27f..59bcbfc4 100644 Binary files a/lib/wlrepo/tests/data/clean/.hg/undo.dirstate and b/lib/wlrepo/tests/data/clean/.hg/undo.dirstate differ diff --git a/lib/wlrepo/tests/data/clean/ignored_file b/lib/wlrepo/tests/data/clean/ignored_file deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/wlrepo/tests/data/clean/pub_valid_file.xml b/lib/wlrepo/tests/data/clean/pub_valid_file.xml deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/wlrepo/tests/data/simple/$meta b/lib/wlrepo/tests/data/simple/$meta new file mode 100644 index 00000000..e69de29b diff --git a/lib/wlrepo/tests/data/simple/.hg/branch b/lib/wlrepo/tests/data/simple/.hg/branch new file mode 100644 index 00000000..4ad96d51 --- /dev/null +++ b/lib/wlrepo/tests/data/simple/.hg/branch @@ -0,0 +1 @@ +default diff --git a/lib/wlrepo/tests/data/simple/.hg/branchheads.cache b/lib/wlrepo/tests/data/simple/.hg/branchheads.cache new file mode 100644 index 00000000..713758bd --- /dev/null +++ b/lib/wlrepo/tests/data/simple/.hg/branchheads.cache @@ -0,0 +1,4 @@ +65046aefea667e38c7a01d7ca62412d7e81f68bc 2 +d5e516ebd357bca24c6afb737e97db3b3d4f111a default +00e4aa6f4f53f31020e4d3e0579cb3c0fa0bd056 $doc:sample +65046aefea667e38c7a01d7ca62412d7e81f68bc $doc:sample_pl diff --git a/lib/wlrepo/tests/data/simple/.hg/dirstate b/lib/wlrepo/tests/data/simple/.hg/dirstate index c13e1c1c..46fefcaa 100644 Binary files a/lib/wlrepo/tests/data/simple/.hg/dirstate and b/lib/wlrepo/tests/data/simple/.hg/dirstate differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/00changelog.i b/lib/wlrepo/tests/data/simple/.hg/store/00changelog.i index 35b7cda0..f2451544 100644 Binary files a/lib/wlrepo/tests/data/simple/.hg/store/00changelog.i and b/lib/wlrepo/tests/data/simple/.hg/store/00changelog.i differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/00manifest.i b/lib/wlrepo/tests/data/simple/.hg/store/00manifest.i index 7eb40cb5..fd65f619 100644 Binary files a/lib/wlrepo/tests/data/simple/.hg/store/00manifest.i and b/lib/wlrepo/tests/data/simple/.hg/store/00manifest.i differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/data/$meta.i b/lib/wlrepo/tests/data/simple/.hg/store/data/$meta.i new file mode 100644 index 00000000..2431023a Binary files /dev/null and b/lib/wlrepo/tests/data/simple/.hg/store/data/$meta.i differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/data/.hgtags.i b/lib/wlrepo/tests/data/simple/.hg/store/data/.hgtags.i new file mode 100644 index 00000000..6c3fcf23 Binary files /dev/null and b/lib/wlrepo/tests/data/simple/.hg/store/data/.hgtags.i differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/data/ignored__file.i b/lib/wlrepo/tests/data/simple/.hg/store/data/ignored__file.i deleted file mode 100644 index a6bdf46f..00000000 Binary files a/lib/wlrepo/tests/data/simple/.hg/store/data/ignored__file.i and /dev/null differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/data/pub__polish__file.xml.i b/lib/wlrepo/tests/data/simple/.hg/store/data/pub__polish__file.xml.i deleted file mode 100644 index 3488bccb..00000000 Binary files a/lib/wlrepo/tests/data/simple/.hg/store/data/pub__polish__file.xml.i and /dev/null differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/data/pub__valid__file.xml.i b/lib/wlrepo/tests/data/simple/.hg/store/data/pub__valid__file.xml.i deleted file mode 100644 index fd3c7f7b..00000000 Binary files a/lib/wlrepo/tests/data/simple/.hg/store/data/pub__valid__file.xml.i and /dev/null differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/data/sample.parts.i b/lib/wlrepo/tests/data/simple/.hg/store/data/sample.parts.i new file mode 100644 index 00000000..a6bdf46f Binary files /dev/null and b/lib/wlrepo/tests/data/simple/.hg/store/data/sample.parts.i differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/data/sample.xml.i b/lib/wlrepo/tests/data/simple/.hg/store/data/sample.xml.i new file mode 100644 index 00000000..b115f4d6 Binary files /dev/null and b/lib/wlrepo/tests/data/simple/.hg/store/data/sample.xml.i differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/data/sample__pl.xml.i b/lib/wlrepo/tests/data/simple/.hg/store/data/sample__pl.xml.i new file mode 100644 index 00000000..920c5742 Binary files /dev/null and b/lib/wlrepo/tests/data/simple/.hg/store/data/sample__pl.xml.i differ diff --git a/lib/wlrepo/tests/data/simple/.hg/store/fncache b/lib/wlrepo/tests/data/simple/.hg/store/fncache index 8730267a..c228181f 100644 --- a/lib/wlrepo/tests/data/simple/.hg/store/fncache +++ b/lib/wlrepo/tests/data/simple/.hg/store/fncache @@ -1,4 +1,6 @@ +data/$meta.i data/.hgignore.i -data/ignored_file.i -data/pub_valid_file.xml.i -data/pub_polish_file.xml.i +data/sample.parts.i +data/sample.xml.i +data/sample_pl.xml.i +data/.hgtags.i diff --git a/lib/wlrepo/tests/data/simple/.hg/store/undo b/lib/wlrepo/tests/data/simple/.hg/store/undo index 62ca7e43..3ad8b327 100644 Binary files a/lib/wlrepo/tests/data/simple/.hg/store/undo and b/lib/wlrepo/tests/data/simple/.hg/store/undo differ diff --git a/lib/wlrepo/tests/data/simple/.hg/strip-backup/26716f931d95-backup b/lib/wlrepo/tests/data/simple/.hg/strip-backup/26716f931d95-backup new file mode 100644 index 00000000..424de921 Binary files /dev/null and b/lib/wlrepo/tests/data/simple/.hg/strip-backup/26716f931d95-backup differ diff --git a/lib/wlrepo/tests/data/simple/.hg/undo.dirstate b/lib/wlrepo/tests/data/simple/.hg/undo.dirstate index fb1041a5..310d9416 100644 Binary files a/lib/wlrepo/tests/data/simple/.hg/undo.dirstate and b/lib/wlrepo/tests/data/simple/.hg/undo.dirstate differ diff --git a/lib/wlrepo/tests/data/simple/.hgtags b/lib/wlrepo/tests/data/simple/.hgtags new file mode 100644 index 00000000..0b061138 --- /dev/null +++ b/lib/wlrepo/tests/data/simple/.hgtags @@ -0,0 +1 @@ +d5e516ebd357bca24c6afb737e97db3b3d4f111a $branchbase diff --git a/lib/wlrepo/tests/data/simple/ignored_file b/lib/wlrepo/tests/data/simple/ignored_file deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/wlrepo/tests/data/simple/pub_polish_file.xml b/lib/wlrepo/tests/data/simple/pub_polish_file.xml deleted file mode 100644 index c7ada58c..00000000 --- a/lib/wlrepo/tests/data/simple/pub_polish_file.xml +++ /dev/null @@ -1 +0,0 @@ -Gąska! diff --git a/lib/wlrepo/tests/data/simple/pub_valid_file.xml b/lib/wlrepo/tests/data/simple/pub_valid_file.xml deleted file mode 100644 index 84321884..00000000 --- a/lib/wlrepo/tests/data/simple/pub_valid_file.xml +++ /dev/null @@ -1 +0,0 @@ -Ala ma kota diff --git a/lib/wlrepo/tests/test_mercurial.py b/lib/wlrepo/tests/test_mercurial.py index 9d37f9fd..09780671 100644 --- a/lib/wlrepo/tests/test_mercurial.py +++ b/lib/wlrepo/tests/test_mercurial.py @@ -6,14 +6,11 @@ __doc__ = "Tests for RAL mercurial backend." from nose.tools import * -import wlrepo from wlrepo import MercurialLibrary -from wlrepo.backend_mercurial import * import os, os.path, tempfile import shutil - REPO_TEMPLATES = os.path.join( os.path.dirname(__file__), 'data') def temprepo(name): @@ -46,191 +43,200 @@ def temprepo(name): def test_opening(library): pass -@temprepo('clean') -def test_main_cabinet(library): - mcab = library.main_cabinet - assert_equal(mcab.maindoc_name, '') - - doclist = mcab.parts() - assert_equal( list(doclist), ['valid_file']) - @temprepo('simple') def test_read_document(library): - doc = library.main_cabinet.retrieve('valid_file') - assert_equal(doc.read().strip(), 'Ala ma kota') + doc = library.document('sample') + assert_equal(doc.data('xml'), 'Ala ma kota\n') @temprepo('simple') def test_read_UTF8_document(library): - doc = library.main_cabinet.retrieve('polish_file') - assert_equal(doc.read().strip(), u'Gąska!'.encode('utf-8')) + doc = library.document('sample_pl') + assert_equal(doc.data('xml'), u'Gżegżółka\n'.encode('utf-8')) @temprepo('simple') -def test_write_document(library): - doc = library.main_cabinet.retrieve('valid_file') - assert_equal(doc.read().strip(), 'Ala ma kota') +def test_change_document(library): + doc = library.document('sample') STRING = u'Gąski lubią pływać!\n'.encode('utf-8') - doc.write(STRING) - assert_equal(doc.read(), STRING) + + def write_action(library, resolve): + f = library._fileopen(resolve('xml'), 'r+') + assert_equal(f.read(), 'Ala ma kota\n') + f.seek(0) + f.write(STRING) + f.flush() + f.seek(0) + assert_equal(f.read(), STRING) + + def commit_info(document): + return ("Document rewrite", "library") + + ndoc = doc.invoke_and_commit(write_action, commit_info) + assert_equal(ndoc.data('xml'), STRING) + @temprepo('simple') def test_create_document(library): - doc = library.main_cabinet.create("another_file", "Some text") - assert_equal( doc.read(), "Some text") - assert_true( os.path.isfile( os.path.join(library.ospath, "pub_another_file.xml")) ) - -@temprepo('branched') -def test_switch_branch(library): - tester_cab = library.cabinet("valid_file", "tester", create=False) - assert_equal( list(tester_cab.parts()), ['valid_file']) - -@raises(wlrepo.CabinetNotFound) -@temprepo('branched') -def test_branch_not_found(library): - tester_cab = library.cabinet("ugh", "tester", create=False) - -@temprepo('branched') -def test_no_branches(library): - n4 = library.shelf(4) - n3 = library.shelf(3) - n2 = library.shelf(2) - n1 = library.shelf(1) - n0 = library.shelf(0) - - assert_true( n3.parentof(n4) ) - assert_false( n4.parentof(n3) ) - assert_true( n0.parentof(n1) ) - assert_false( n1.parentof(n0) ) - assert_false( n0.parentof(n4) ) - -# def test_ancestor_of_simple(self): - assert_true( n3.ancestorof(n4) ) - assert_true( n2.ancestorof(n4) ) - assert_true( n1.ancestorof(n4) ) - assert_true( n0.ancestorof(n4) ) - - assert_true( n2.ancestorof(n3) ) - assert_true( n1.ancestorof(n3) ) - assert_true( n0.ancestorof(n3) ) - - assert_false( n4.ancestorof(n4) ) - assert_false( n4.ancestorof(n3) ) - assert_false( n3.ancestorof(n2) ) - assert_false( n3.ancestorof(n1) ) - assert_false( n3.ancestorof(n0) ) - -# def test_common_ancestor_simple(self): - assert_true( n3.has_common_ancestor(n4) ) - assert_true( n3.has_common_ancestor(n3) ) - assert_true( n3.has_common_ancestor(n3) ) - - -@temprepo('branched2') -def test_once_branched(library): - n7 = library.shelf(7) - n6 = library.shelf(6) - n5 = library.shelf(5) - n4 = library.shelf(4) - n3 = library.shelf(3) - n2 = library.shelf(2) - - assert_true( n2.parentof(n3) ) - assert_false( n3.parentof(n2) ) - - assert_true( n2.parentof(n5) ) - assert_false( n5.parentof(n2) ) - - assert_false( n2.parentof(n4) ) - assert_false( n2.parentof(n6) ) - assert_false( n3.parentof(n5) ) - assert_false( n5.parentof(n3) ) - -# def test_ancestorof_branched(self): - assert_true( n2.ancestorof(n7) ) - assert_false( n7.ancestorof(n2) ) - assert_true( n2.ancestorof(n6) ) - assert_false( n6.ancestorof(n2) ) - assert_true( n2.ancestorof(n5) ) - assert_false( n5.ancestorof(n2) ) - - assert_false( n3.ancestorof(n5) ) - assert_false( n5.ancestorof(n3) ) - assert_false( n4.ancestorof(n5) ) - assert_false( n5.ancestorof(n4) ) - assert_false( n3.ancestorof(n7) ) - assert_false( n7.ancestorof(n3) ) - assert_false( n4.ancestorof(n6) ) - assert_false( n6.ancestorof(n4) ) - -# def test_common_ancestor_branched(self): - assert_true( n2.has_common_ancestor(n4) ) - assert_true( n2.has_common_ancestor(n7) ) - assert_true( n2.has_common_ancestor(n6) ) - - # cause it's not in the right branch - assert_false( n5.has_common_ancestor(n3) ) - assert_false( n7.has_common_ancestor(n4) ) - -@temprepo('merged') -def test_after_merge(library): - n8 = library.shelf(8) - n7 = library.shelf(7) - n6 = library.shelf(6) - - assert_true( n7.parentof(n8) ) - assert_false( n8.parentof(n7) ) - - assert_true( n7.ancestorof(n8) ) - assert_true( n6.ancestorof(n8) ) - - - assert_true( n7.has_common_ancestor(n8) ) - # cause it's not in the right branch - assert_false( n8.has_common_ancestor(n7) ) - -@temprepo('merged_with_local_commit') -def test_after_merge_and_local_commit(library): - n9 = library.shelf(9) - n8 = library.shelf(8) - n7 = library.shelf(7) - n6 = library.shelf(6) - - assert_true( n7.parentof(n8) ) - assert_false( n8.parentof(n7) ) - - assert_true( n9.has_common_ancestor(n8) ) - # cause it's not in the right branch - assert_false( n8.has_common_ancestor(n9) ) - - -@temprepo('branched2') -def test_merge_personal_to_default(library): - main = library.shelf(2) - print main - - local = library.shelf(7) - print local + assert_equal(sorted(library.documents()), sorted(['sample', 'sample_pl'])) - document = library.document("ala", "admin") - shared = document.shared() - assert_true( shared is None ) - document.share("Here is my copy!") - - assert_equal( document.shelf(), local) # local didn't change - - shared = document.shared() - assert_true( shared is not None ) - - print library.shelf() - - new_main = shared.shelf() - assert_not_equal( new_main, main) # main has new revision - - # check for parents - assert_true( main.parentof(new_main) ) - assert_true( local.parentof(new_main) ) - -@temprepo('clean') -def test_create_branch(library): - tester_cab = library.cabinet("anotherone", "tester", create=True) - assert_equal( list(tester_cab.parts()), ['anotherone']) + doc = library.document_create("creation") + doc.quickwrite("xml", "", "Quick write", user="library") + + assert_equal(sorted(library.documents()), sorted(['sample', 'sample_pl', 'creation'])) + +# +#@temprepo('branched') +#def test_switch_branch(library): +# tester_cab = library.cabinet("valid_file", "tester", create=False) +# assert_equal( list(tester_cab.parts()), ['valid_file']) +# +#@raises(wlrepo.CabinetNotFound) +#@temprepo('branched') +#def test_branch_not_found(library): +# tester_cab = library.cabinet("ugh", "tester", create=False) +# +#@temprepo('branched') +#def test_no_branches(library): +# n4 = library.shelf(4) +# n3 = library.shelf(3) +# n2 = library.shelf(2) +# n1 = library.shelf(1) +# n0 = library.shelf(0) +# +# assert_true( n3.parentof(n4) ) +# assert_false( n4.parentof(n3) ) +# assert_true( n0.parentof(n1) ) +# assert_false( n1.parentof(n0) ) +# assert_false( n0.parentof(n4) ) +# +## def test_ancestor_of_simple(self): +# assert_true( n3.ancestorof(n4) ) +# assert_true( n2.ancestorof(n4) ) +# assert_true( n1.ancestorof(n4) ) +# assert_true( n0.ancestorof(n4) ) +# +# assert_true( n2.ancestorof(n3) ) +# assert_true( n1.ancestorof(n3) ) +# assert_true( n0.ancestorof(n3) ) +# +# assert_false( n4.ancestorof(n4) ) +# assert_false( n4.ancestorof(n3) ) +# assert_false( n3.ancestorof(n2) ) +# assert_false( n3.ancestorof(n1) ) +# assert_false( n3.ancestorof(n0) ) +# +## def test_common_ancestor_simple(self): +# assert_true( n3.has_common_ancestor(n4) ) +# assert_true( n3.has_common_ancestor(n3) ) +# assert_true( n3.has_common_ancestor(n3) ) +# +# +#@temprepo('branched2') +#def test_once_branched(library): +# n7 = library.shelf(7) +# n6 = library.shelf(6) +# n5 = library.shelf(5) +# n4 = library.shelf(4) +# n3 = library.shelf(3) +# n2 = library.shelf(2) +# +# assert_true( n2.parentof(n3) ) +# assert_false( n3.parentof(n2) ) +# +# assert_true( n2.parentof(n5) ) +# assert_false( n5.parentof(n2) ) +# +# assert_false( n2.parentof(n4) ) +# assert_false( n2.parentof(n6) ) +# assert_false( n3.parentof(n5) ) +# assert_false( n5.parentof(n3) ) +# +## def test_ancestorof_branched(self): +# assert_true( n2.ancestorof(n7) ) +# assert_false( n7.ancestorof(n2) ) +# assert_true( n2.ancestorof(n6) ) +# assert_false( n6.ancestorof(n2) ) +# assert_true( n2.ancestorof(n5) ) +# assert_false( n5.ancestorof(n2) ) +# +# assert_false( n3.ancestorof(n5) ) +# assert_false( n5.ancestorof(n3) ) +# assert_false( n4.ancestorof(n5) ) +# assert_false( n5.ancestorof(n4) ) +# assert_false( n3.ancestorof(n7) ) +# assert_false( n7.ancestorof(n3) ) +# assert_false( n4.ancestorof(n6) ) +# assert_false( n6.ancestorof(n4) ) +# +## def test_common_ancestor_branched(self): +# assert_true( n2.has_common_ancestor(n4) ) +# assert_true( n2.has_common_ancestor(n7) ) +# assert_true( n2.has_common_ancestor(n6) ) +# +# # cause it's not in the right branch +# assert_false( n5.has_common_ancestor(n3) ) +# assert_false( n7.has_common_ancestor(n4) ) +# +#@temprepo('merged') +#def test_after_merge(library): +# n8 = library.shelf(8) +# n7 = library.shelf(7) +# n6 = library.shelf(6) +# +# assert_true( n7.parentof(n8) ) +# assert_false( n8.parentof(n7) ) +# +# assert_true( n7.ancestorof(n8) ) +# assert_true( n6.ancestorof(n8) ) +# +# +# assert_true( n7.has_common_ancestor(n8) ) +# # cause it's not in the right branch +# assert_false( n8.has_common_ancestor(n7) ) +# +#@temprepo('merged_with_local_commit') +#def test_after_merge_and_local_commit(library): +# n9 = library.shelf(9) +# n8 = library.shelf(8) +# n7 = library.shelf(7) +# n6 = library.shelf(6) +# +# assert_true( n7.parentof(n8) ) +# assert_false( n8.parentof(n7) ) +# +# assert_true( n9.has_common_ancestor(n8) ) +# # cause it's not in the right branch +# assert_false( n8.has_common_ancestor(n9) ) +# +# +#@temprepo('branched2') +#def test_merge_personal_to_default(library): +# main = library.shelf(2) +# print main +# +# local = library.shelf(7) +# print local +# +# document = library.document("ala", "admin") +# shared = document.shared() +# assert_true( shared is None ) +# document.share("Here is my copy!") +# +# assert_equal( document.shelf(), local) # local didn't change +# +# shared = document.shared() +# assert_true( shared is not None ) +# +# print library.shelf() +# +# new_main = shared.shelf() +# assert_not_equal( new_main, main) # main has new revision +# +# # check for parents +# assert_true( main.parentof(new_main) ) +# assert_true( local.parentof(new_main) ) +# +#@temprepo('clean') +#def test_create_branch(library): +# tester_cab = library.cabinet("anotherone", "tester", create=True) +# assert_equal( list(tester_cab.parts()), ['anotherone']) diff --git a/project/settings.py b/project/settings.py index 0e9ba8d8..1a064762 100644 --- a/project/settings.py +++ b/project/settings.py @@ -82,7 +82,7 @@ MIDDLEWARE_CLASSES = ( ROOT_URLCONF = 'urls' TEMPLATE_DIRS = ( - PROJECT_ROOT + '/templates' + PROJECT_ROOT + '/templates', ) # CSS and JS files to compress diff --git a/project/templates/manager/pull_request.html b/project/templates/manager/pull_request.html new file mode 100644 index 00000000..c03caab3 --- /dev/null +++ b/project/templates/manager/pull_request.html @@ -0,0 +1,7 @@ +{% if objects %} + {% for pullreq in objects %} +

{{ pullreq }}

+ {% endfor %} +{% else %} +

Brak żądań

+{% endif %} diff --git a/project/templates/toolbar_api/scriptlets.js b/project/templates/toolbar_api/scriptlets.js new file mode 100644 index 00000000..ae7e2df4 --- /dev/null +++ b/project/templates/toolbar_api/scriptlets.js @@ -0,0 +1,15 @@ +function SciptletCenter() { + + this.scriptlets = { + {% for scriptlet in scriptlets %} + "{{scriptlet.name}}": function(context, params) { + {{scriptlet.code|safe}} + }, + {% endfor %} + + _none: null + }; + +} + +scriptletCenter = new ScriptletCenter(); \ No newline at end of file diff --git a/project/templates/wysiwyg.html b/project/templates/wysiwyg.html new file mode 100644 index 00000000..409e4f25 --- /dev/null +++ b/project/templates/wysiwyg.html @@ -0,0 +1,92 @@ +{% extends 'base.html' %} + +{% block extrahead %} + +{% endblock %} + +{% block maincontent %} +

Wysiwyg editor

+
This part is not editable!
+
+ +

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Suspendisse a urna eu enim rutrum elementum nec sed nibh. Quisque sed tortor + nulla, et euismod turpis. Morbi purus nulla, vulputate in vulputate id, gravida + id eros. In interdum est tempor est consequat imperdiet. Vivamus vitae ligula quam. + Proin nibh quam, tincidunt sit amet auctor eget, laoreet sit amet eros. Cras augue + lectus, euismod nec posuere ac, ultricies sed magna. Aliquam a lacinia sapien. + Cras imperdiet urna vel dui accumsan mollis. Suspendisse convallis tincidunt ornare. + Aenean convallis libero in lectus dictum vestibulum interdum ipsum suscipit. + Suspendisse sed justo sapien, eu egestas libero. Sed tincidunt sagittis sollicitudin. + Aliquam erat volutpat. Nullam egestas dolor id massa sagittis at sagittis ipsum + hendrerit. +

+ +

Phasellus sed purus non orci eleifend posuere ac eu elit. Etiam orci justo, porta vitae varius in, scelerisque sed metus. Phasellus faucibus lorem at metus scelerisque sit amet sollicitudin dolor dignissim. Aliquam eu justo in diam blandit posuere at a diam. Pellentesque tristique sem eu odio gravida eleifend. Phasellus cursus adipiscing metus, nec pharetra enim pharetra ac. Pellentesque faucibus volutpat lorem nec vulputate. Mauris in faucibus ipsum. Nulla ut urna nulla. Sed at tellus nec diam posuere porttitor. Duis faucibus, libero nec rhoncus facilisis, tortor ligula adipiscing massa, nec varius justo ante et magna. Donec orci mauris, ultrices nec blandit vel, lacinia in ante. Maecenas libero mi, pretium id ultricies eget, fringilla sit amet risus. Integer ut ante sem, et condimentum odio. Nam nec est erat. Etiam ut metus ligula. In vel condimentum orci.

+ +

Suspendisse potenti. Proin in augue nibh. Curabitur in sollicitudin ipsum. In ut leo vel purus volutpat tempus. Proin ut neque at augue euismod ullamcorper nec ac dui. Vestibulum id quam nunc, eu porta augue. Pellentesque interdum neque eu nulla rhoncus vulputate. Sed viverra diam ac sem consectetur semper. Quisque consectetur fringilla quam, in feugiat nisl vulputate quis. Fusce vel ipsum lectus, eu interdum nunc. Donec luctus libero vitae mauris imperdiet at iaculis magna aliquet. Curabitur ullamcorper, diam nec pulvinar venenatis, nibh ante volutpat mauris, nec tristique lectus sem in urna. Aenean eu malesuada metus. Integer auctor nulla sit amet ligula sollicitudin ut accumsan velit ullamcorper. Donec nec auctor augue.

+ +

Maecenas eget lacus vitae velit tincidunt bibendum quis et diam. In ullamcorper condimentum velit, et elementum felis vestibulum facilisis. Donec vitae cursus ipsum. Cras accumsan tincidunt aliquet. Nulla pellentesque mattis magna aliquet hendrerit. Pellentesque pellentesque odio enim. Duis viverra rhoncus tristique. In in risus ligula. Nullam dapibus lacinia facilisis. Ut eu neque neque, tristique laoreet nisl. Aliquam placerat dignissim leo, tristique tempor est vestibulum ut.

+ +

Donec semper tempus ante, eget gravida erat varius et. Suspendisse aliquam rutrum nunc ac pulvinar. Aliquam erat volutpat. Nulla consectetur ultricies imperdiet. Nulla tincidunt est vitae augue porttitor a faucibus odio facilisis. In nec nisl odio. Aliquam et libero tortor, eu tincidunt mi. Vivamus suscipit erat sed mi hendrerit fringilla. Integer iaculis tempus nulla, at egestas velit faucibus ut. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vestibulum vel massa enim. Aliquam erat volutpat. In ligula tortor, fermentum eu suscipit at, posuere vel nunc. Nullam nibh magna, sollicitudin at semper et, mattis ut quam. Curabitur accumsan semper elit ac posuere. Sed sit amet lorem tortor, vel porttitor justo. Fusce odio metus, bibendum ut bibendum sit amet, luctus a ipsum.

+
+{% endblock %} + +{% block extrabody %} + +{% endblock %} \ No newline at end of file