From: Lukasz Rekucki Date: Sun, 8 Nov 2009 13:49:11 +0000 (+0100) Subject: Merge branch 'master' into lqc-trunk X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/061f517181630ee982c33aee63965b18965fd5aa?ds=inline;hp=-c Merge branch 'master' into lqc-trunk --- 061f517181630ee982c33aee63965b18965fd5aa diff --combined apps/api/handlers/library_handlers.py index 27ff47d3,7e90e313..e2d986ee --- a/apps/api/handlers/library_handlers.py +++ b/apps/api/handlers/library_handlers.py @@@ -4,12 -4,8 +4,8 @@@ import os.pat import logging log = logging.getLogger('platforma.api.library') - __author__= "Łukasz Rekucki" - __date__ = "$2009-09-25 15:49:50$" - __doc__ = "Module documentation." - from piston.handler import BaseHandler, AnonymousBaseHandler - from django.http import HttpResponse + from piston.utils import rc from datetime import date @@@ -19,10 -15,8 +15,8 @@@ from django.db import IntegrityErro import librarian import librarian.html import difflib - from librarian import dcparser, parser ++import wlrepo - import wlrepo - from api.models import PullRequest -from wlrepo import * from explorer.models import GalleryForDocument # internal imports @@@ -157,7 -151,7 +151,7 @@@ class LibraryHandler(BaseHandler) import traceback # rollback branch creation lib._rollback() - raise LibraryException(traceback.format_exc()) + raise wlrepo.LibraryException(traceback.format_exc()) url = reverse('document_view', args=[doc.id]) @@@ -169,12 -163,12 +163,12 @@@ url = url ) finally: lock.release() - except LibraryException, e: + except wlrepo.LibraryException, e: import traceback return response.InternalError().django_response({ "reason": traceback.format_exc() }) - except DocumentAlreadyExists: + except wlrepo.DocumentAlreadyExists: # Document is already there return response.EntityConflict().django_response({ "reason": "already-exists", @@@ -184,27 -178,6 +178,7 @@@ # # Document Handlers # - class BasicDocumentHandler(AnonymousBaseHandler): - allowed_methods = ('GET',) - - @hglibrary - def read(self, request, docid, lib): - try: - doc = lib.document(docid) - except wlrepo.RevisionNotFound: - return rc.NOT_FOUND - - result = { - 'name': doc.id, - 'html_url': reverse('dochtml_view', args=[doc.id]), - 'text_url': reverse('doctext_view', args=[doc.id]), - 'dc_url': reverse('docdc_view', args=[doc.id]), - 'public_revision': doc.revision, - } - - return result - + class DiffHandler(BaseHandler): allowed_methods = ('GET',) @@@ -233,7 -206,6 +207,6 @@@ # class DocumentHandler(BaseHandler): allowed_methods = ('GET', 'PUT') - anonymous = BasicDocumentHandler @validate_form(forms.DocumentRetrieveForm, 'GET') @hglibrary @@@ -250,7 -222,7 +223,7 @@@ try: doc = lib.document(docid, user, rev=rev) - except RevisionMismatch, e: + except wlrepo.RevisionMismatch, e: # the document exists, but the revision is bad return response.EntityNotFound().django_response({ 'reason': 'revision-mismatch', @@@ -258,7 -230,7 +231,7 @@@ 'docid': docid, 'user': user, }) - except RevisionNotFound, e: + except wlrepo.RevisionNotFound, e: # the user doesn't have this document checked out # or some other weird error occured # try to do the checkout @@@ -278,7 -250,7 +251,7 @@@ 'docid': docid, 'user': user, }) - except RevisionNotFound, e: + except wlrepo.RevisionNotFound, e: return response.EntityNotFound().django_response({ 'reason': 'document-not-found', 'message': e.message, @@@ -341,7 -313,7 +314,7 @@@ class DocumentHTMLHandler(BaseHandler) "with-paths": 'boolean(1)', }) - except (EntryNotFound, RevisionNotFound), e: + except (wlrepo.EntryNotFound, wlrepo.RevisionNotFound), e: return response.EntityNotFound().django_response({ 'reason': 'not-found', 'message': e.message}) except librarian.ValidationError, e: @@@ -356,7 -328,7 +329,7 @@@ # class DocumentGalleryHandler(BaseHandler): - allowed_methods = ('GET') + allowed_methods = ('GET', 'POST') def read(self, request, docid): @@@ -401,7 -373,27 +374,27 @@@ return galleries - + def create(self, request, docid): + if not request.user.is_superuser: + return rc.FORBIDDEN + + new_path = request.POST.get('path') + + if new_path: + gallery, created = GalleryForDocument.objects.get_or_create( + document = docid, + defaults = { + 'subpath': new_path, + } + ) + + if not created: + gallery.subpath = new_path + gallery.save() + + return rc.CREATED + + return rc.BAD_REQUEST # # Dublin Core handlers diff --combined apps/explorer/views.py index fd99f27e,5ec452ae..a67d27ca mode 100755,100644..100755 --- a/apps/explorer/views.py +++ b/apps/explorer/views.py @@@ -11,12 -11,12 +11,14 @@@ from django.core.urlresolvers import re from django.http import HttpResponse from django.utils import simplejson as json from django.views.generic.simple import direct_to_template +from django.shortcuts import render_to_response +from django.template import RequestContext from django.contrib.auth.decorators import login_required + from explorer import forms from api.models import PullRequest + def ajax_login_required(view): """Similar ro @login_required, but instead of redirect, just return some JSON stuff with error.""" @@@ -30,15 -30,14 +32,16 @@@ @login_required def display_editor(request, path): user = request.GET.get('user', request.user.username) - log.info(user) - + gallery_form = forms.GalleryChoiceForm() + - return direct_to_template(request, 'explorer/editor.html', extra_context={ + return render_to_response('explorer/editor.html', - mimetype="text/html", ++ # mimetype="text/html", + dictionary = { 'fileid': path, - 'euser': user + 'euser': user, - 'gallery_form': gallery_form, - }) - ++ 'gallery_from': gallery_form, + }, context_instance=RequestContext(request)) - ++ # # View all files # @@@ -120,11 -119,3 +123,11 @@@ def pull_requests(request) return direct_to_template(request, 'manager/pull_request.html', extra_context = {'objects': objects} ) + + +# +# Testing +# +def renderer_test(request): + return direct_to_template(request, 'renderer.html', mimetype="application/xhtml+xml", - extra_context = {} ) ++ extra_context = {} ) diff --combined platforma/static/js/models.js index d0e7df13,2c36e2bb..59a285a1 --- a/platforma/static/js/models.js +++ b/platforma/static/js/models.js @@@ -36,192 -36,87 +36,192 @@@ Editor.ToolbarButtonsModel = Editor.Mod }); -// Stany modelu: // -// -> error -> loading -// / -// empty -> loading -> synced -> unsynced -> loading -// \ -// -> dirty -> updating -> updated -> synced +// HTML Document Model // -Editor.XMLModel = Editor.Model.extend({ - _className: 'Editor.XMLModel', - serverURL: null, - data: '', +Editor.HTMLModel = Editor.Model.extend({ + _className: 'Editor.HTMLModel', + textURL: null, state: 'empty', - - init: function(document, serverURL) { + + init: function(document, textURL) { this._super(); this.set('state', 'empty'); this.set('revision', document.get('revision')); this.document = document; - this.serverURL = serverURL; - this.toolbarButtonsModel = new Editor.ToolbarButtonsModel(); + + this.textURL = textURL; + + this.htmlXSL = null; + this.wlmlXSL = null; + this.rawText = null; + + // create a parser and a serializer + this.parser = new DOMParser(); + this.serializer = new XMLSerializer(); + this.addObserver(this, 'data', this.dataChanged.bind(this)); }, - + load: function(force) { if (force || this.get('state') == 'empty') { this.set('state', 'loading'); - messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...'); + messageCenter.addMessage('info', 'xmlload', 'Wczytuję HTML...'); + + // request all stylesheets $.ajax({ - url: this.serverURL, + url: documentInfo.staticURL + 'xsl/wl2html_client.xsl', + dataType: 'xml', + success: this.htmlXSLLoadSuccess.bind(this), + error: this.loadingFailed.bind(this) + }); + + $.ajax({ + url: documentInfo.staticURL + 'xsl/html2wl_client.xsl', + dataType: 'xml', + success: this.wlmlXSLLoadSuccess.bind(this), + error: this.loadingFailed.bind(this) + }); + + $.ajax({ + url: this.textURL, dataType: 'text', data: { revision: this.get('revision'), user: this.document.get('user') }, - success: this.loadingSucceeded.bind(this), + success: this.textLoadSuccess.bind(this), error: this.loadingFailed.bind(this) }); return true; } return false; }, - - loadingSucceeded: function(data) { + + asWLML: function(element) + { + console.log("Source", element); + var doc = this.parser.parseFromString(this.serializer.serializeToString(element), 'text/xml'); + + var result = this.wlmlXSL.transformToDocument(doc); + + if(!result) { + console.log("Failed", this.wlmlXSL, doc); + throw "Failed to transform fragment"; + } + + console.log("Transformed", doc, " to: ", result.documentElement); + return this.serializer.serializeToString(result.documentElement); + }, + + updateWithWLML: function($element, text) + { + // filter the string + text = text.replace(/\/\s+/g, '
'); + var chunk = this.parser.parseFromString(""+text+"", "text/xml"); + + var errors = $('parsererror', chunk); + + // check if chunk is parsable + if(errors.length > 0) + throw {text: errors.text(), html: errors.html()}; + + var result = this.htmlXSL.transformToFragment(chunk, document); + + console.log("RESULT", this.serializer.serializeToString(result)); + + if(!result) + throw "WLML->HTML transformation failed."; + + $element.replaceWith(result); + this.set('state', 'dirty'); + }, + + createXSLT: function(xslt_doc) { + var p = new XSLTProcessor(); + p.importStylesheet(xslt_doc); + return p; + }, + + htmlXSLLoadSuccess: function(data) + { + try { + this.htmlXSL = this.createXSLT(data); + + if(this.wlmlXSL && this.htmlXSL && this.rawText) + this.loadSuccess(); + } catch(e) { + this.loadingFailed(); + } + }, + + wlmlXSLLoadSuccess: function(data) + { + try { + this.wlmlXSL = this.createXSLT(data); + + if(this.wlmlXSL && this.htmlXSL && this.rawText) + this.loadSuccess(); + } catch(e) { + this.loadingFailed(); + } + }, + + textLoadSuccess: function(data) { + this.rawText = data; + + if(this.wlmlXSL && this.htmlXSL && this.rawText) + this.loadSuccess(); + }, + + loadSuccess: function() { if (this.get('state') != 'loading') { alert('erroneous state:', this.get('state')); } - this.set('data', data); + + // prepare text + var doc = null; + doc = this.rawText.replace(/\/\s+/g, '
'); + doc = this.parser.parseFromString(doc, 'text/xml'); + doc = this.htmlXSL.transformToFragment(doc, document).firstChild; + + this.set('data', doc); this.set('state', 'synced'); - messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)'); + messageCenter.addMessage('success', 'xmlload', 'Wczytałem HTML :-)'); }, - + loadingFailed: function(response) { if (this.get('state') != 'loading') { alert('erroneous state:', this.get('state')); } - + var message = parseXHRError(response); - + this.set('error', '

Błąd przy ładowaniu XML

'+message+'

'); this.set('state', 'error'); - messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-('); + messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać HTML. Spróbuj ponownie :-('); }, - + save: function(message) { if (this.get('state') == 'dirty') { - this.set('state', 'updating'); - messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...'); - + this.set('state', 'saving'); + + messageCenter.addMessage('info', 'htmlsave', 'Zapisuję HTML...'); + var wlml = this.asWLML(this.get('data')); + var payload = { - contents: this.get('data'), + contents: wlml, revision: this.get('revision'), user: this.document.get('user') }; + if (message) { payload.message = message; } - + $.ajax({ - url: this.serverURL, + url: this.textURL, type: 'post', dataType: 'json', data: payload, @@@ -232,24 -127,24 +232,24 @@@ } return false; }, - + saveSucceeded: function(data) { - if (this.get('state') != 'updating') { + if (this.get('state') != 'saving') { alert('erroneous state:', this.get('state')); } this.set('revision', data.revision); this.set('state', 'updated'); - messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)'); + messageCenter.addMessage('success', 'htmlsave', 'Zapisałem :-)'); }, - + saveFailed: function() { - if (this.get('state') != 'updating') { + if (this.get('state') != 'saving') { alert('erroneous state:', this.get('state')); } - messageCenter.addMessage('error', 'xmlsave', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-('); + messageCenter.addMessage('error', 'htmlsave', 'Nie udało mi się zapisać.'); this.set('state', 'dirty'); }, - + // For debbuging set: function(property, value) { if (property == 'state') { @@@ -257,13 -152,13 +257,13 @@@ } return this._super(property, value); }, - + dataChanged: function(property, value) { if (this.get('state') == 'synced') { this.set('state', 'dirty'); } }, - + dispose: function() { this.removeObserver(this); this._super(); @@@ -271,36 -166,36 +271,36 @@@ }); -Editor.HTMLModel = Editor.Model.extend({ - _className: 'Editor.HTMLModel', - dataURL: null, - htmlURL: null, - renderURL: null, - displaData: '', - xmlParts: {}, +// Stany modelu: +// +// -> error -> loading +// / +// empty -> loading -> synced -> unsynced -> loading +// \ +// -> dirty -> updating -> updated -> synced +// +Editor.XMLModel = Editor.Model.extend({ + _className: 'Editor.XMLModel', + serverURL: null, + data: '', state: 'empty', - init: function(document, dataURL, htmlURL) { + init: function(document, serverURL) { this._super(); this.set('state', 'empty'); - this.set('revision', document.get('revision')); - + this.set('revision', document.get('revision')); this.document = document; - this.htmlURL = htmlURL; - this.dataURL = dataURL; - this.renderURL = documentInfo.renderURL; - this.xmlParts = {}; + this.serverURL = serverURL; + this.toolbarButtonsModel = new Editor.ToolbarButtonsModel(); + this.addObserver(this, 'data', this.dataChanged.bind(this)); }, load: function(force) { if (force || this.get('state') == 'empty') { this.set('state', 'loading'); - - // load the transformed data - // messageCenter.addMessage('info', 'Wczytuję HTML...'); - + messageCenter.addMessage('info', 'xmlload', 'Wczytuję XML...'); $.ajax({ - url: this.htmlURL, + url: this.serverURL, dataType: 'text', data: { revision: this.get('revision'), @@@ -309,10 -204,8 +309,10 @@@ success: this.loadingSucceeded.bind(this), error: this.loadingFailed.bind(this) }); + return true; } - }, + return false; + }, loadingSucceeded: function(data) { if (this.get('state') != 'loading') { @@@ -320,38 -213,98 +320,38 @@@ } this.set('data', data); this.set('state', 'synced'); + messageCenter.addMessage('success', 'xmlload', 'Wczytałem XML :-)'); }, - loadingFailed: function(response) { + loadingFailed: function(response) + { if (this.get('state') != 'loading') { alert('erroneous state:', this.get('state')); } - var err = parseXHRError(response); + var message = parseXHRError(response); - this.set('error', '

Nie udało się wczytać widoku HTML:

' + err.error_message); - this.set('state', 'error'); - }, - - getXMLPart: function(elem, callback) - { - var path = elem.attr('x-pointer'); - if(!this.xmlParts[path]) - this.loadXMLPart(elem, callback); - else - callback(path, this.xmlParts[path]); - }, - - loadXMLPart: function(elem, callback) - { - var path = elem.attr('x-pointer'); - var self = this; - - $.ajax({ - url: this.dataURL, - dataType: 'text', - data: { - revision: this.get('revision'), - user: this.document.get('user'), - chunk: path - // format: 'nl' - }, - success: function(data) { - self.xmlParts[path] = data; - console.log(data); - callback(path, data); - }, - // TODO: error handling - error: function(data) { - console.log('Failed to load fragment'); - callback(undefined, undefined); - } - }); - }, - - putXMLPart: function(elem, data, callback) { - var self = this; - - var path = elem.attr('x-pointer'); - this.xmlParts[path] = data; - - this.set('state', 'dirty'); - - /* re-render the changed fragment */ - $.ajax({ - url: this.renderURL, - type: "POST", - dataType: 'text; charset=utf-8', - data: { - fragment: data, - chunk: path - // format: 'nl' - }, - success: function(htmldata) { - callback(elem, htmldata); - self.set('state', 'dirty'); - } - }); + this.set('error', '

Błąd przy ładowaniu XML

'+message+'

'); + this.set('state', 'error'); + messageCenter.addMessage('error', 'xmlload', 'Nie udało mi się wczytać XML. Spróbuj ponownie :-('); }, - + save: function(message) { if (this.get('state') == 'dirty') { this.set('state', 'updating'); - + messageCenter.addMessage('info', 'xmlsave', 'Zapisuję XML...'); + var payload = { - chunks: $.toJSON(this.xmlParts), + contents: this.get('data'), revision: this.get('revision'), user: this.document.get('user') }; - if (message) { payload.message = message; } - - console.log(payload) - + $.ajax({ - url: this.dataURL, + url: this.serverURL, type: 'post', dataType: 'json', data: payload, @@@ -361,45 -314,38 +361,45 @@@ return true; } return false; - }, - + saveSucceeded: function(data) { if (this.get('state') != 'updating') { alert('erroneous state:', this.get('state')); } - - // flush the cache - this.xmlParts = {}; - this.set('revision', data.revision); this.set('state', 'updated'); + messageCenter.addMessage('success', 'xmlsave', 'Zapisałem XML :-)'); }, - + saveFailed: function() { if (this.get('state') != 'updating') { alert('erroneous state:', this.get('state')); - } + } + messageCenter.addMessage('error', 'xmlsave', 'Nie udało mi się zapisać XML. Spróbuj ponownie :-('); this.set('state', 'dirty'); }, - + // For debbuging set: function(property, value) { if (property == 'state') { console.log(this.description(), ':', property, '=', value); } return this._super(property, value); + }, + + dataChanged: function(property, value) { + if (this.get('state') == 'synced') { + this.set('state', 'dirty'); + } + }, + + dispose: function() { + this.removeObserver(this); + this._super(); } }); - Editor.ImageGalleryModel = Editor.Model.extend({ _className: 'Editor.ImageGalleryModel', serverURL: null, @@@ -414,6 -360,22 +414,22 @@@ this.pages = []; }, + setGallery: function(path) { + $.ajax({ + url: this.serverURL, + type: 'post', + data: { + path: path, + }, + success: this.settingGallerySucceeded.bind(this) + }); + }, + + settingGallerySucceeded: function(data) { + console.log('settingGallerySucceeded'); + this.load(true); + }, + load: function(force) { if (force || this.get('state') == 'empty') { console.log("setting state"); @@@ -503,7 -465,7 +519,7 @@@ Editor.DocumentModel = Editor.Model.ext this.contentModels = { 'xml': new Editor.XMLModel(this, data.text_url), - 'html': new Editor.HTMLModel(this, data.text_url, data.html_url), + 'html': new Editor.HTMLModel(this, data.text_url), 'gallery': new Editor.ImageGalleryModel(this, data.gallery_url) }; @@@ -576,7 -538,7 +592,7 @@@ revision: this.get('revision'), user: this.get('user') }, - complete: this.updateCompleted.bind(this), + complete: this.updateCompleted.bind(this) }); }, diff --combined platforma/static/js/views/gallery.js index 31b47cec,a403c9ef..d6844c62 mode 100755,100644..100755 --- a/platforma/static/js/views/gallery.js +++ b/platforma/static/js/views/gallery.js @@@ -10,8 -10,7 +10,8 @@@ var ImageGalleryView = View.extend( init: function(element, model, parent, template) { console.log("init for gallery"); - this._super(element, model, template); + var submodel = model.contentModels['gallery']; + this._super(element, submodel, template); this.parent = parent; console.log("gallery model", this.model); @@@ -26,12 -25,15 +26,15 @@@ }, modelDataChanged: function(property, value) - { - if( property == 'data') - { - this.render(); - this.gotoPage(this.currentPage); - } + { + console.log(this.model.get('state'), value, value.length); + if ((this.model.get('state') == 'synced') && (value.length == 0)) { + console.log('tutaj'); + this.render('image-gallery-empty-template'); + } else { + this.render(); + this.gotoPage(this.currentPage); + } }, gotoPage: function(index) @@@ -86,8 -88,11 +89,11 @@@ modelStateChanged: function(property, value) { if (value == 'loading') { - // this.freeze('Ładowanie...'); + this.freeze('Ładowanie...'); } else { + if ((value == 'synced') && (this.model.get('data').length == 0)) { + this.render('image-gallery-empty-template'); + } this.unfreeze(); } }, @@@ -211,23 -216,26 +217,26 @@@ mousedown(this.pageDragStart.bind(this)); }, - render: function() + render: function(template) { - if(!this.model) return; - - /* first unbind all */ - if(this.$nextButton) this.$nextButton.unbind(); - if(this.$prevButton) this.$prevButton.unbind(); - if(this.$jumpButton) this.$jumpButton.unbind(); - if(this.$pageInput) this.$pageInput.unbind(); - - if(this.$zoomInButton) this.$zoomInButton.unbind(); - if(this.$zoomOutButton) this.$zoomOutButton.unbind(); - if(this.$zoomResetButton) this.$zoomResetButton.unbind(); - + if(!this.model) return; + + $('.choose-gallery-button', this.element).unbind(); + + /* first unbind all */ + if(this.$nextButton) this.$nextButton.unbind(); + if(this.$prevButton) this.$prevButton.unbind(); + if(this.$jumpButton) this.$jumpButton.unbind(); + if(this.$pageInput) this.$pageInput.unbind(); + + if(this.$zoomInButton) this.$zoomInButton.unbind(); + if(this.$zoomOutButton) this.$zoomOutButton.unbind(); + if(this.$zoomResetButton) this.$zoomResetButton.unbind(); + + if (!template) { /* render */ this._super(); - + /* fetch important parts */ this.$pageListRoot = $('.image-gallery-page-list', this.element); this.$pages = $('.image-gallery-page-container', this.$pageListRoot); @@@ -253,6 -261,15 +262,15 @@@ this.gotoPage(this.currentPage); this.changePageZoom(this.pageZoom); + } else { + this._super(template); + + var self = this; + $('.choose-gallery-button', self.element).click(function() { + console.log('CLICK CLICK') + self.model.setGallery($('#id_subpath', self.element).val()); + }); + } }, jumpToPage: function() { diff --combined platforma/templates/explorer/editor.html index f6a76307,8930c944..2e8a6b4b --- a/platforma/templates/explorer/editor.html +++ b/platforma/templates/explorer/editor.html @@@ -12,8 -12,9 +12,8 @@@ docID: '{{ fileid }}', userID: '{{ euser }}', docURL: '{% url document_view fileid %}{% if euser %}?user={{ euser|urlencode }}{% endif %}', - toolbarURL: '{% url toolbar_buttons %}', - renderURL: '{% url api.views.render %}', - staticURL: '{{ STATIC_URL }}' + toolbarURL: '{% url toolbar_buttons %}', + staticURL: '{{ STATIC_URL }}' } @@@ -64,13 -65,23 +64,13 @@@ - - - - + +