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
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
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])
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",
#
# 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',)
#
class DocumentHandler(BaseHandler):
allowed_methods = ('GET', 'PUT')
- anonymous = BasicDocumentHandler
@validate_form(forms.DocumentRetrieveForm, 'GET')
@hglibrary
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',
'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
'docid': docid,
'user': user,
})
- except RevisionNotFound, e:
+ except wlrepo.RevisionNotFound, e:
return response.EntityNotFound().django_response({
'reason': 'document-not-found',
'message': e.message,
"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:
#
class DocumentGalleryHandler(BaseHandler):
- allowed_methods = ('GET')
+ allowed_methods = ('GET', 'POST')
def read(self, request, docid):
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
});
-// 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, '<br />');
+ var chunk = this.parser.parseFromString("<chunk>"+text+"</chunk>", "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, '<br />');
+ 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', '<h2>Błąd przy ładowaniu XML</h2><p>'+message+'</p>');
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,
}
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') {
}
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.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'),
success: this.loadingSucceeded.bind(this),
error: this.loadingFailed.bind(this)
});
+ return true;
}
- },
+ return false;
+ },
loadingSucceeded: function(data) {
if (this.get('state') != 'loading') {
}
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', '<p>Nie udało się wczytać widoku HTML: </p>' + 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', '<h2>Błąd przy ładowaniu XML</h2><p>'+message+'</p>');
+ 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,
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,
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");
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)
};
revision: this.get('revision'),
user: this.get('user')
},
- complete: this.updateCompleted.bind(this),
+ complete: this.updateCompleted.bind(this)
});
},