From: zuber Date: Sat, 10 Oct 2009 23:10:20 +0000 (+0200) Subject: Merge branch 'master' of stigma.nowoczesnapolska.org.pl:platforma X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/46fdcc29313a6c0dd897b8060335061f433897fd?hp=aee39efde819f0381a7aa65bc32145d2709cf088 Merge branch 'master' of stigma.nowoczesnapolska.org.pl:platforma Conflicts: fabfile.py --- diff --git a/apps/api/handlers/library_handlers.py b/apps/api/handlers/library_handlers.py index 4435fe96..488c2d40 100644 --- a/apps/api/handlers/library_handlers.py +++ b/apps/api/handlers/library_handlers.py @@ -38,6 +38,7 @@ log = logging.getLogger('platforma.api') # # Document List Handlers # +# TODO: security check class BasicLibraryHandler(AnonymousBaseHandler): allowed_methods = ('GET',) @@ -51,10 +52,14 @@ class BasicLibraryHandler(AnonymousBaseHandler): return {'documents' : document_list} +# +# This handler controlls the document collection +# class LibraryHandler(BaseHandler): allowed_methods = ('GET', 'POST') anonymous = BasicLibraryHandler + @hglibrary def read(self, request, lib): """Return the list of documents.""" @@ -138,12 +143,15 @@ class LibraryHandler(BaseHandler): lock.release() except LibraryException, e: import traceback - return response.InternalError().django_response(\ - {'exception': traceback.format_exc()} ) + return response.InternalError().django_response({ + "reason": traceback.format_exc() + }) except DocumentAlreadyExists: # Document is already there - return response.EntityConflict().django_response(\ - {"reason": "Document %s already exists." % docid}) + return response.EntityConflict().django_response({ + "reason": "already-exists", + "message": "Document already exists." % docid + }) # # Document Handlers @@ -216,7 +224,7 @@ class DocumentHTMLHandler(BaseHandler): allowed_methods = ('GET') @hglibrary - def read(self, request, docid, lib): + def read(self, request, docid, lib, stylesheet='partial'): """Read document as html text""" try: revision = request.GET.get('revision', 'latest') @@ -231,7 +239,7 @@ class DocumentHTMLHandler(BaseHandler): 'message': 'Provided revision refers, to document "%s", but provided "%s"' % (document.id, docid) }) return librarian.html.transform(document.data('xml'), is_file=False, \ - parse_dublincore=False, stylesheet="partial",\ + parse_dublincore=False, stylesheet=stylesheet,\ options={ "with-paths": 'boolean(1)', }) @@ -318,7 +326,8 @@ class DocumentTextHandler(BaseHandler): # we're done :) return document.data('xml') else: - xdoc = parser.WLDocument.from_string(document.data('xml')) + xdoc = parser.WLDocument.from_string(document.data('xml'),\ + parse_dublincore=False) ptext = xdoc.part_as_text(part) if ptext is None: @@ -547,33 +556,35 @@ class MergeHandler(BaseHandler): "provided": target_rev, "latest": udoc.revision }) - if not request.user.has_perm('explorer.document.can_share'): - # User is not permitted to make a merge, right away - # So we instead create a pull request in the database - try: - prq, created = PullRequest.get_or_create( - source_revision = str(udoc.revision), - defaults = { - 'comitter': request.user, - 'document': docid, - 'status': "N", - 'comment': form.cleaned_data['message'] or '$AUTO$ Document shared.', - } - ) - - return response.RequestAccepted().django_response(\ - ticket_status=prq.status, \ - ticket_uri=reverse("pullrequest_view", args=[prq.id]) ) - except IntegrityError, e: - return response.InternalError().django_response() - if form.cleaned_data['type'] == 'update': # update is always performed from the file branch # to the user branch success, changed = udoc.update(request.user.username) - if form.cleaned_data['type'] == 'share': - success, changed = udoc.share(form.cleaned_data['message']) + if form.cleaned_data['type'] == 'share': + if not request.user.has_perm('explorer.document.can_share'): + # User is not permitted to make a merge, right away + # So we instead create a pull request in the database + try: + prq, created = PullRequest.objects.get_or_create( + source_revision = str(udoc.revision), + defaults = { + 'comitter': request.user, + 'document': docid, + 'status': "N", + 'comment': form.cleaned_data['message'] or '$AUTO$ Document shared.', + } + ) + + return response.RequestAccepted().django_response(\ + ticket_status=prq.status, \ + ticket_uri=reverse("pullrequest_view", args=[prq.id]) ) + except IntegrityError: + return response.EntityConflict().django_response({ + 'reason': 'request-already-exist' + }) + else: + success, changed = udoc.share(form.cleaned_data['message']) if not success: return response.EntityConflict().django_response({ diff --git a/apps/api/handlers/manage_handlers.py b/apps/api/handlers/manage_handlers.py index 96159582..b3e2760b 100644 --- a/apps/api/handlers/manage_handlers.py +++ b/apps/api/handlers/manage_handlers.py @@ -6,7 +6,9 @@ __doc__ = "Module documentation." from piston.handler import BaseHandler, AnonymousBaseHandler +from api.utils import hglibrary from explorer.models import PullRequest +from api.response import * class PullRequestListHandler(BaseHandler): allowed_methods = ('GET',) @@ -19,7 +21,64 @@ class PullRequestListHandler(BaseHandler): class PullRequestHandler(BaseHandler): - allowed_methods = ('GET',) + allowed_methods = ('GET', 'PUT') def read(self, request, prq_id): - return PullRequest.objects.get(id=prq_id) \ No newline at end of file + return PullRequest.objects.get(id=prq_id) + + def update(self, request, prq_id): + """Change the status of request""" + + if not request.user.has_perm('explorer.document.can_share'): + return AccessDenied().django_response("Insufficient priviliges") + + prq = PullRequest.objects.get(id=prq_id) + + if not prq: + return EntityNotFound().django_response() + + + action = request.PUT.get('action', None) + + if action == 'accept' and prq.status == 'N': + return self.accept_merge(prq) + elif action == 'reject' and prq.status in ['N', 'R']: + return self.reject_merge(prq) + else: + return BadRequest().django_response() + + + @hglibrary + def accept_merge(self, prq, lib): + doc = lib.document( prq.document ) + udoc = doc.take( prq.comitter.username ) + success, changed = udoc.share(prq.comment) + + if not success: + return EntityConflict().django_response() + + doc = doc.latest() + + prq.status = 'A' + prq.merged_revisions = unicode(doc.revision) + prq.save() + + return SuccessAllOk().django_response({ + 'status': prq.status + }) + + + def reject_merge(self, prq, lib): + prq.status = 'R' + prq.save() + + return SuccessAllOk().django_response({ + 'status': prq.status + }) + + + + + + + diff --git a/apps/api/handlers/toolbar_handlers.py b/apps/api/handlers/toolbar_handlers.py index be773590..5408db3b 100644 --- a/apps/api/handlers/toolbar_handlers.py +++ b/apps/api/handlers/toolbar_handlers.py @@ -17,9 +17,7 @@ class ToolbarHandler(BaseHandler): 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 ] - + return [g.to_dict(with_buttons=True) for g in groups] class ScriptletsHandler(BaseHandler): allowed_methods = ('GET',) diff --git a/apps/api/urls.py b/apps/api/urls.py index deac319a..5464374f 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -30,11 +30,11 @@ urlpatterns = patterns('', # Pull requests url(r"^pull-requests$", pullrequest_collection, - {'emitter_format': 'json'} ), + {'emitter_format': 'json'}, name="pullrequest_list" ), url(r"^pull-requests/(?P\d+)$", pullrequest_rsrc, - {'emitter_format': 'json'}, name="pullrequest_view" ), - + {'emitter_format': 'json'}, name="pullrequest_view" ), + # Documents url(r'^documents$', library_resource, {'emitter_format': 'json'}, name="document_list_view"), diff --git a/apps/explorer/views.py b/apps/explorer/views.py index 7a42b432..124a5743 100644 --- a/apps/explorer/views.py +++ b/apps/explorer/views.py @@ -116,6 +116,8 @@ def file_upload(request, repo): @login_required def print_html(request, **kwargs): from api.resources import document_html_resource + + kwargs['stylesheet'] = 'legacy' output = document_html_resource.handler.read(request, **kwargs) @@ -145,6 +147,14 @@ def _get_issues_for_file(fileid): # ================= # = Pull requests = # ================= -#def pull_requests(request): -# return direct_to_template(request, 'manager/pull_request.html', extra_context = { -# 'objects': models.PullRequest.objects.all()} ) +def pull_requests(request): + from explorer.models import PullRequest + + objects = PullRequest.objects.order_by('status') + + if not request.user.has_perm('explorer.book.can_share'): + objects = objects.filter(comitter=request.user) + + + return direct_to_template(request, 'manager/pull_request.html', + extra_context = {'objects': objects} ) diff --git a/apps/toolbar/models.py b/apps/toolbar/models.py index 04146223..1b986c3e 100644 --- a/apps/toolbar/models.py +++ b/apps/toolbar/models.py @@ -14,6 +14,13 @@ class ButtonGroup(models.Model): def __unicode__(self): return self.name + def to_dict(self, with_buttons=False): + d = {'name': self.name, 'position': self.position} + + if with_buttons: + d['buttons'] = [ b.to_dict() for b in self.button_set.all() ] + + return d #class ButtonGroupManager(models.Manager): # @@ -67,8 +74,18 @@ class Button(models.Model): if self.key_mod & 0x01: mods.append('Alt') if self.key_mod & 0x02: mods.append('Ctrl') if self.key_mod & 0x04: mods.append('Shift') - mods.append('"'+self.key+'"') - return '+'.join(mods) + mods.append(str(self.key)) + return '[' + '+'.join(mods) + ']' + + def to_dict(self): + return { + 'label': self.label, + 'tooltip': (self.tooltip or '') + self.hotkey_name(), + 'key': self.key, + 'key_mod': self.key_mod, + 'params': self.params, + 'scriptlet_id': self.scriptlet_id + } def __unicode__(self): return self.label diff --git a/fabfile.py b/fabfile.py index c06c50b5..6304aaea 100644 --- a/fabfile.py +++ b/fabfile.py @@ -1,4 +1,6 @@ -from fabric.api import run, local, settings, abort, env, cd, require, abort +from __future__ import with_statement +from fabric.api import * + def staging(): '''Use staging server''' diff --git a/lib/wlrepo/mercurial_backend/__init__.py b/lib/wlrepo/mercurial_backend/__init__.py index f919e817..630939f7 100644 --- a/lib/wlrepo/mercurial_backend/__init__.py +++ b/lib/wlrepo/mercurial_backend/__init__.py @@ -79,6 +79,7 @@ class MercurialRevision(wlrepo.Revision): return bool(self._library._hgrepo.changelog.children(self.hgrev())) def merge_with(self, other, user, message): + message = self._library._sanitize_string(message) lock = self._library.lock(True) try: self._library._checkout(self._changectx.node()) diff --git a/lib/wlrepo/mercurial_backend/document.py b/lib/wlrepo/mercurial_backend/document.py index e7145616..a8f7adc3 100644 --- a/lib/wlrepo/mercurial_backend/document.py +++ b/lib/wlrepo/mercurial_backend/document.py @@ -115,7 +115,8 @@ class MercurialDocument(wlrepo.Document): return (True, False) - return self._revision.merge_with(sv._revision, user=user) + return self._revision.merge_with(sv._revision, user=user, + message="$AUTO$ Personal branch update.") finally: lock.release() @@ -160,6 +161,9 @@ class MercurialDocument(wlrepo.Document): if not local.parentof(main): success, changed = main.merge_with(local, user=user, message=message) + success = True + changed = False + # Case 3: # main * # | @@ -179,6 +183,9 @@ class MercurialDocument(wlrepo.Document): if not local.parentof(main): success, changed = local.merge_with(main, user=user, \ message='$AUTO$ Local branch update during share.') + + success = True + changed = False else: print "case 4" diff --git a/project/settings.py b/project/settings.py index 143f2926..3a1f3ad9 100644 --- a/project/settings.py +++ b/project/settings.py @@ -8,6 +8,7 @@ TEMPLATE_DEBUG = DEBUG ADMINS = ( (u'Marek Stępniowski', 'marek@stepniowski.com'), + (u'Łukasz Rekucki', 'lrekucki@gmail.com'), ) MANAGERS = ADMINS @@ -30,8 +31,8 @@ TIME_ZONE = 'Europe/Warsaw Poland' # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = 'pl' -import locale -locale.setlocale(locale.LC_ALL, '') +#import locale +#locale.setlocale(locale.LC_ALL, '') SITE_ID = 1 @@ -115,7 +116,8 @@ INSTALLED_APPS = ( 'django.contrib.sites', 'django.contrib.admin', 'django.contrib.admindocs', - + + 'piston', 'explorer', 'toolbar', 'api', diff --git a/project/static/css/html_print.css b/project/static/css/html_print.css new file mode 100644 index 00000000..38cb5968 --- /dev/null +++ b/project/static/css/html_print.css @@ -0,0 +1,229 @@ +/* Style widoku HTML. Nie należy tu ustawiać position ani marginesów */ +.htmlview { + font-size: 16px; + font: Georgia, "Times New Roman", serif; + line-height: 1.5em; + padding: 3em; +} + +.htmlview div { + max-width: 36em; +} + +.htmlview #toc { + display: none; +} + +.htmlview a { + color: blue; + text-decoration: none; +} + +.htmlview h1 { + font-size: 3em; + margin: 1.5em 0; + text-align: center; + line-height: 1.5em; + font-weight: bold; +} + +.htmlview h2 { + font-size: 2em; + margin: 1.5em 0 0; + font-weight: bold; + line-height: 1.5em; +} + +.htmlview h3 { + font-size: 1.5em; + margin: 1.5em 0 0; + font-weight: normal; + line-height: 1.5em; +} + +.htmlview h4 { + font-size: 1em; + margin: 1.5em 0 0; + line-height: 1.5em; +} + +.htmlview p { + margin: 0; +} + +/* ======================== */ +/* = Footnotes and themes = */ +/* ======================== */ +.htmlview .theme-begin { + border-left: 0.1em solid #DDDDDD; + color: #777; + padding: 0 0.5em; + width: 7.5em; + font-style: normal; + font-weight: normal; + font-size: 16px; + float: right; + margin-right: -9.5em; + clear: both; + left: 40em; + line-height: 1.5em; + text-align: left; +} + +.htmlview .annotation { + font-style: normal; + font-weight: normal; + font-size: 12px; +} + +.htmlview #footnotes .annotation { + display: block; + float: left; + width: 2.5em; + clear: both; +} + +.htmlview #footnotes div { + margin: 1.5em 0 0 0; +} + +.htmlview #footnotes p { + margin-left: 2.5em; + font-size: 0.875em; +} + +.htmlview blockquote { + font-size: 0.875em; +} + +/* ============= */ +/* = Numbering = */ +/* ============= */ +.htmlview p { + position: relative; +} + +.htmlview .anchor { + position: absolute; + margin: 0em; + left: -3em; + color: #777; + font-size: 12px; + width: 2em; + text-align: center; + padding: 0.25em 0.5em; + line-height: 1.5em; +} + +.htmlview .anchor:hover, .htmlview .anchor:active { + color: #FFF; + background-color: #CCC; +} + +/* =================== */ +/* = Custom elements = */ +/* =================== */ +.htmlview span.author { + font-size: 0.5em; + display: block; + line-height: 1.5em; + margin-bottom: 0.25em; +} + +.htmlview span.collection { + font-size: 0.375em; + display: block; + line-height: 1.5em; + margin-bottom: -0.25em; +} + +.htmlview span.subtitle { + font-size: 0.5em; + display: block; + line-height: 1.5em; + margin-top: -0.25em; +} + +.htmlview div.didaskalia { + font-style: italic; + margin: 0.5em 0 0 1.5em; +} + +.htmlview div.kwestia { + margin: 0.5em 0 0; +} + +.htmlview div.stanza { + margin: 1.5em 0 0; +} + +.htmlview div.kwestia div.stanza { + margin: 0; +} + +.htmlview p.paragraph { + text-align: justify; + margin: 1.5em 0 0; +} + +.htmlview p.motto { + text-align: justify; + font-style: italic; + margin: 1.5em 0 0; +} + +.htmlview p.motto_podpis { + font-size: 0.875em; + text-align: right; +} + +.htmlview div.fragment { + border-bottom: 0.1em solid #999; + padding-bottom: 1.5em; +} + +.htmlview div.note p, .htmlview div.dedication p, +.htmlview div.note p.paragraph, .htmlview div.dedication p.paragraph { + text-align: right; + font-style: italic; +} + +.htmlview hr.spacer { + height: 3em; + visibility: hidden; +} + +.htmlview hr.spacer-line { + margin: 1.5em 0; + border: none; + border-bottom: 0.1em solid #000; +} + +.htmlview p.spacer-asterisk { + padding: 0; + margin: 1.5em 0; + text-align: center; +} + +.htmlview div.person-list ol { + list-style: none; + padding: 0 0 0 1.5em; +} + +.htmlview p.place-and-time { + font-style: italic; +} + +.htmlview em.math, .htmlview em.foreign-word, +.htmlview em.book-title, .htmlview em.didaskalia { + font-style: italic; +} + +.htmlview em.author-emphasis { + letter-spacing: 0.1em; +} + +.htmlview em.person { + font-style: normal; + font-variant: small-caps; +} diff --git a/project/static/css/managment.css b/project/static/css/managment.css new file mode 100644 index 00000000..3777bcfc --- /dev/null +++ b/project/static/css/managment.css @@ -0,0 +1,36 @@ +table.request-report +{ + border: 2px groove black; + font-size: 12pt; + + margin-top: 3em; + margin-bottom: 2em; + margin-left: auto; + margin-right: auto; +} + +.request-report td, .request-report th { + vertical-align: top; + border-right: 1px solid black; + border-bottom: 1px solid black; + + padding: 0.4em 1em; + margin: 0em; +} + +.request-report th { + background-color: black; + color: white; +} + +.request-report .status-N { + background-color: teal; +} + +.request-report .status-R { + background-color: red; +} + +.request-report .status-A { + background-color: gray; +} \ No newline at end of file diff --git a/project/static/js/app.js b/project/static/js/app.js index 9de3c7f0..fde15393 100644 --- a/project/static/js/app.js +++ b/project/static/js/app.js @@ -49,20 +49,21 @@ if (typeof console === 'undefined') { this.render_template = function render_template(str, data){ // Figure out if we're getting a template, or if we need to - // load the template - and be sure to cache the result. + // load the template - and be sure to cache the result. var fn = !/^[\d\s-_]/.test(str) ? cache[str] = cache[str] || render_template(document.getElementById(str).innerHTML) : // Generate a reusable function that will serve as a template // generator (and which will be cached). + new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + // Introduce the data as local variables using with(){} "with(obj){p.push('" + - // Convert the template into pure JavaScript + // Convert the template into pure JavaScript str .replace(/[\r\t\n]/g, " ") .split("<%").join("\t") diff --git a/project/static/js/models.js b/project/static/js/models.js index e2ee8b15..ab021f51 100644 --- a/project/static/js/models.js +++ b/project/static/js/models.js @@ -23,7 +23,15 @@ Editor.ToolbarButtonsModel = Editor.Model.extend({ } }, - loadSucceeded: function(data) { + loadSucceeded: function(data) + { + // do some escaping + $.each(data, function() { + $.each(this.buttons, function() { + //do some lame escapes + this.tooltip = this.tooltip.replace(/"/g, """); + }); + }); this.set('buttons', data); } }); @@ -176,7 +184,7 @@ Editor.HTMLModel = Editor.Model.extend({ this.set('state', 'loading'); // load the transformed data - messageCenter.addMessage('info', 'Wczytuję HTML...'); + // messageCenter.addMessage('info', 'Wczytuję HTML...'); $.ajax({ url: this.htmlURL, @@ -196,16 +204,42 @@ Editor.HTMLModel = Editor.Model.extend({ } this.set('data', data); this.set('state', 'synced'); - messageCenter.addMessage('success', 'Wczytałem HTML :-)'); + // messageCenter.addMessage('success', 'Wczytałem HTML :-)'); }, - loadingFailed: function() { + loadingFailed: function(response) { if (this.get('state') != 'loading') { alert('erroneous state:', this.get('state')); } - this.set('error', 'Nie udało się załadować panelu'); + + var json_response = null; + var message = ""; + + try { + json_response = $.evalJSON(response.responseText); + + if(json_response.reason == 'xml-parse-error') { + + message = json_response.message.replace(/(line\s+)(\d+)(\s+)/i, + "$1$2$3"); + + message = message.replace(/(line\s+)(\d+)(\,\s*column\s+)(\d+)/i, + "$1$2$3$4"); + + + } + else { + message = json_response.message || json_response.reason || "nieznany błąd."; + } + } + catch (e) { + message = response.statusText; + } + + this.set('error', '

Nie udało się wczytać widoku HTML:

' + message); + this.set('state', 'error'); - messageCenter.addMessage('error', 'Nie udało mi się wczytać HTML. Spróbuj ponownie :-('); + // messageCenter.addMessage('error', 'Nie udało mi się wczytać HTML. Spróbuj ponownie :-('); }, getXMLPart: function(elem, callback) @@ -224,7 +258,7 @@ Editor.HTMLModel = Editor.Model.extend({ $.ajax({ url: this.dataURL, - dataType: 'text; charset=utf-8', + dataType: 'text', data: { revision: this.get('revision'), part: path @@ -548,12 +582,12 @@ $(function() toolbarUrl = $('#api-toolbar-url').text(); doc = new Editor.DocumentModel(); - var editor = new EditorView('#body-wrap', doc); - editor.freeze(); - var flashView = new FlashView('#flashview', messageCenter); - var splitView = new SplitView('#splitview', doc); + EditorView = new EditorView('#body-wrap', doc); + EditorView.freeze(); leftPanelView = new PanelContainerView('#left-panel-container', doc); rightPanelContainer = new PanelContainerView('#right-panel-container', doc); + + var flashView = new FlashView('#flashview', messageCenter); }); diff --git a/project/static/js/views/button_toolbar.js b/project/static/js/views/button_toolbar.js index 7e4b6eab..7f039985 100644 --- a/project/static/js/views/button_toolbar.js +++ b/project/static/js/views/button_toolbar.js @@ -47,6 +47,7 @@ var ButtonToolbarView = View.extend({ buttonPressed: function(event) { + var self = this; var target = event.target; var groupIndex = parseInt($(target).attr('ui:groupindex'), 10); @@ -57,9 +58,13 @@ var ButtonToolbarView = View.extend({ console.log('Executing', scriptletId, 'with params', params); try { - scriptletCenter.scriptlets[scriptletId](this.parent, params); + self.parent.freeze('Wykonuję akcję...'); + setTimeout(function() { + scriptletCenter.scriptlets[scriptletId](self.parent, params); + self.parent.unfreeze(); + }, 10); } catch(e) { - console.log("Scriptlet", scriptletId, "failed."); + console.log("Scriptlet", scriptletId, "failed.", e); } }, diff --git a/project/static/js/views/editor.js b/project/static/js/views/editor.js index 10c77f63..2793141f 100644 --- a/project/static/js/views/editor.js +++ b/project/static/js/views/editor.js @@ -1,134 +1,142 @@ /*global View render_template panels */ var EditorView = View.extend({ - _className: 'EditorView', - element: null, - model: null, - template: null, + _className: 'EditorView', + element: null, + model: null, + template: null, - init: function(element, model, template) { - this._super(element, model, template); + init: function(element, model, template) { + this._super(element, model, template); - this.quickSaveButton = $('#action-quick-save', this.element).bind('click.editorview', this.quickSave.bind(this)); - this.commitButton = $('#action-commit', this.element).bind('click.editorview', this.commit.bind(this)); - this.updateButton = $('#action-update', this.element).bind('click.editorview', this.update.bind(this)); - this.mergeButton = $('#action-merge', this.element).bind('click.editorview', this.merge.bind(this)); + this.quickSaveButton = $('#action-quick-save', this.element).bind('click.editorview', this.quickSave.bind(this)); + this.commitButton = $('#action-commit', this.element).bind('click.editorview', this.commit.bind(this)); + this.updateButton = $('#action-update', this.element).bind('click.editorview', this.update.bind(this)); + this.mergeButton = $('#action-merge', this.element).bind('click.editorview', this.merge.bind(this)); - this.model.addObserver(this, 'state', this.modelStateChanged.bind(this)); - this.modelStateChanged('state', this.model.get('state')); - - // Inicjalizacja okien jQuery Modal - $('#commit-dialog', this.element). - jqm({ - modal: true, - onShow: this.loadRelatedIssues.bind(this) - }); + this.model.addObserver(this, 'state', this.modelStateChanged.bind(this)); + this.modelStateChanged('state', this.model.get('state')); + + this.splitView = new SplitView('#splitview', doc); + + // Inicjalizacja okien jQuery Modal + $('#commit-dialog', this.element). + jqm({ + modal: true, + onShow: this.loadRelatedIssues.bind(this) + }); - $('#commit-dialog-cancel-button', this.element).click(function() { - $('#commit-dialog-error-empty-message').hide(); - $('#commit-dialog').jqmHide(); - }); + $('#commit-dialog-cancel-button', this.element).click(function() { + $('#commit-dialog-error-empty-message').hide(); + $('#commit-dialog').jqmHide(); + }); - // $('#split-dialog').jqm({ - // modal: true, - // onShow: $.fbind(self, self.loadSplitDialog) - // }). - // jqmAddClose('button.dialog-close-button'); + // $('#split-dialog').jqm({ + // modal: true, + // onShow: $.fbind(self, self.loadSplitDialog) + // }). + // jqmAddClose('button.dialog-close-button'); - this.model.load(); - }, + this.model.load(); + }, - quickSave: function(event) { - this.model.saveDirtyContentModel(); - }, + quickSave: function(event) { + this.model.saveDirtyContentModel(); + }, - commit: function(event) { - $('#commit-dialog', this.element).jqmShow({callback: this.doCommit.bind(this)}); - }, + commit: function(event) { + $('#commit-dialog', this.element).jqmShow({ + callback: this.doCommit.bind(this) + }); + }, - doCommit: function(message) { - this.model.saveDirtyContentModel(message); - }, + doCommit: function(message) { + this.model.saveDirtyContentModel(message); + }, - update: function(event) { - this.model.update(); - }, + update: function(event) { + this.model.update(); + }, - merge: function(event) { - $('#commit-dialog', this.element).jqmShow({callback: this.doMerge.bind(this)}); - }, + merge: function(event) { + $('#commit-dialog', this.element).jqmShow({ + callback: this.doMerge.bind(this) + }); + }, - doMerge: function(message) { - this.model.merge(message); - }, + doMerge: function(message) { + this.model.merge(message); + }, - loadRelatedIssues: function(hash) { - var self = this; - var c = $('#commit-dialog-related-issues'); + loadRelatedIssues: function(hash) { + var self = this; + var c = $('#commit-dialog-related-issues'); - $('#commit-dialog-save-button').click(function(event, data) - { - if ($('#commit-dialog-message').val().match(/^\s*$/)) { - $('#commit-dialog-error-empty-message').fadeIn(); - } else { - $('#commit-dialog-error-empty-message').hide(); - $('#commit-dialog').jqmHide(); + $('#commit-dialog-save-button').click(function(event, data) + { + if ($('#commit-dialog-message').val().match(/^\s*$/)) { + $('#commit-dialog-error-empty-message').fadeIn(); + } else { + $('#commit-dialog-error-empty-message').hide(); + $('#commit-dialog').jqmHide(); - var message = $('#commit-dialog-message').val(); - $('#commit-dialog-related-issues input:checked') - .each(function() { message += ' refs #' + $(this).val(); }); - console.log("COMMIT APROVED", hash.t); - hash.t.callback(message); - } - return false; - }); + var message = $('#commit-dialog-message').val(); + $('#commit-dialog-related-issues input:checked') + .each(function() { + message += ' refs #' + $(this).val(); + }); + console.log("COMMIT APROVED", hash.t); + hash.t.callback(message); + } + return false; + }); - $("div.loading-box", c).show(); - $("div.fatal-error-box", c).hide(); - $("div.container-box", c).hide(); + $("div.loading-box", c).show(); + $("div.fatal-error-box", c).hide(); + $("div.container-box", c).hide(); - $.getJSON(c.attr('ui:ajax-src') + '?callback=?', - function(data, status) - { - var fmt = ''; - $(data).each( function() { - fmt += '\n'; - }); - $("div.container-box", c).html(fmt); - $("div.loading-box", c).hide(); - $("div.container-box", c).show(); - }); + $.getJSON(c.attr('ui:ajax-src') + '?callback=?', + function(data, status) + { + var fmt = ''; + $(data).each( function() { + fmt += '\n'; + }); + $("div.container-box", c).html(fmt); + $("div.loading-box", c).hide(); + $("div.container-box", c).show(); + }); - hash.w.show(); - }, + hash.w.show(); + }, - modelStateChanged: function(property, value) { - // Uaktualnia stan przycisków - if (value == 'dirty') { - this.quickSaveButton.attr('disabled', null); - this.commitButton.attr('disabled', null); - this.updateButton.attr('disabled', 'disabled'); - this.mergeButton.attr('disabled', 'disabled'); - } else if (value == 'synced') { - this.quickSaveButton.attr('disabled', 'disabled'); - this.commitButton.attr('disabled', 'disabled'); - this.updateButton.attr('disabled', null); - this.mergeButton.attr('disabled', null); - } else if (value == 'empty') { - this.quickSaveButton.attr('disabled', 'disabled'); - this.commitButton.attr('disabled', 'disabled'); - this.updateButton.attr('disabled', 'disabled'); - this.mergeButton.attr('disabled', 'disabled'); - } - }, + modelStateChanged: function(property, value) { + // Uaktualnia stan przycisków + if (value == 'dirty') { + this.quickSaveButton.attr('disabled', null); + this.commitButton.attr('disabled', null); + this.updateButton.attr('disabled', 'disabled'); + this.mergeButton.attr('disabled', 'disabled'); + } else if (value == 'synced') { + this.quickSaveButton.attr('disabled', 'disabled'); + this.commitButton.attr('disabled', 'disabled'); + this.updateButton.attr('disabled', null); + this.mergeButton.attr('disabled', null); + } else if (value == 'empty') { + this.quickSaveButton.attr('disabled', 'disabled'); + this.commitButton.attr('disabled', 'disabled'); + this.updateButton.attr('disabled', 'disabled'); + this.mergeButton.attr('disabled', 'disabled'); + } + }, - dispose: function() { - $('#action-quick-save', this.element).unbind('click.editorview'); - $('#action-commit', this.element).unbind('click.editorview'); - $('#action-update', this.element).unbind('click.editorview'); - $('#action-merge', this.element).unbind('click.editorview'); + dispose: function() { + $('#action-quick-save', this.element).unbind('click.editorview'); + $('#action-commit', this.element).unbind('click.editorview'); + $('#action-update', this.element).unbind('click.editorview'); + $('#action-merge', this.element).unbind('click.editorview'); - this.model.removeObserver(this); - this._super(); - } + this.model.removeObserver(this); + this._super(); + } }); diff --git a/project/static/js/views/html.js b/project/static/js/views/html.js index ebdf90af..a3db1d28 100644 --- a/project/static/js/views/html.js +++ b/project/static/js/views/html.js @@ -25,7 +25,10 @@ var HTMLView = View.extend({ this.$printLink.attr('href', base + "?revision=" + this.model.get('revision')); }, - modelStateChanged: function(property, value) { + modelStateChanged: function(property, value) + { + var self = $(this); + if (value == 'synced' || value == 'dirty') { this.unfreeze(); } else if (value == 'unsynced') { @@ -36,6 +39,21 @@ var HTMLView = View.extend({ this.freeze('Zapisywanie...'); } else if (value == 'error') { this.freeze(this.model.get('error')); + $('.xml-editor-ref', this.overlay).click( + function(event) { + console.log("Sending scroll rq.", this); + try { + var href = $(this).attr('href').split('-'); + var line = parseInt(href[1]); + var column = parseInt(href[2]); + + $(document).trigger('xml-scroll-request', {line:line, column:column}); + } catch(e) { + console.log(e); + } + + return false; + }); } }, @@ -46,6 +64,9 @@ var HTMLView = View.extend({ this._super(); this.$printLink = $('.html-print-link', this.element); + var base = this.$printLink.attr('ui:baseref'); + this.$printLink.attr('href', base + "?revision=" + this.model.get('revision')); + this.element.bind('click', this.itemClicked.bind(this)); }, diff --git a/project/static/js/views/split.js b/project/static/js/views/split.js index 48f0de73..30eda4ab 100644 --- a/project/static/js/views/split.js +++ b/project/static/js/views/split.js @@ -17,7 +17,7 @@ var SplitView = View.extend({ init: function(element, model) { this._super(element, model, null); this.element.css('position', 'relative'); - this._resizingSubviews = false; + this._resizingSubviews = false; this.views = $(">*", this.element[0]).css({ position: 'absolute', // positioned inside splitter container @@ -28,6 +28,7 @@ var SplitView = View.extend({ this.leftView = $(this.views[0]); this.rightView = $(this.views[1]); + this.splitbar = $(this.views[2] || '
') .insertAfter(this.leftView) .css({ diff --git a/project/static/js/views/xml.js b/project/static/js/views/xml.js index 00547d19..1681caee 100644 --- a/project/static/js/views/xml.js +++ b/project/static/js/views/xml.js @@ -1,4 +1,4 @@ -/*global View CodeMirror ButtonToolbarView render_template panels */ +/*global View CodeMirror ToolbarView render_template panels */ var XMLView = View.extend({ _className: 'XMLView', element: null, @@ -18,8 +18,11 @@ var XMLView = View.extend({ var self = this; $('.xmlview-toolbar', this.element).bind('resize.xmlview', this.resized.bind(this)); - - + + // scroll to the given position (if availble) + this.scrollCallback = this.scrollOnRequest.bind(this); + $(document).bind('xml-scroll-request', this.scrollCallback); + this.parent.freeze('Ładowanie edytora...'); this.editor = new CodeMirror($('.xmlview', this.element).get(0), { parserfile: 'parsexml.js', @@ -28,7 +31,7 @@ var XMLView = View.extend({ parserConfig: { useHTMLKludges: false }, - textWrapping: false, + textWrapping: true, tabMode: 'spaces', indentUnit: 0, onChange: this.editorDataChanged.bind(this), @@ -91,6 +94,8 @@ var XMLView = View.extend({ }, dispose: function() { + $(document).unbind('xml-scroll-request', this.scrollCallback); + this.model.removeObserver(this); $(this.editor.frame).remove(); this._super(); @@ -104,7 +109,7 @@ var XMLView = View.extend({ var ch = String.fromCharCode(code & 0xff).toLowerCase(); /* # console.log(ch.charCodeAt(0), '#', buttons); */ - var buttons = $('.buttontoolbarview-button[title='+ch+']', this.element); + var buttons = $('.buttontoolbarview-button[hotkey='+ch+']', this.element); var mod = 0; if(event.altKey) mod |= 0x01; @@ -141,6 +146,16 @@ var XMLView = View.extend({ this.buttonToolbar.buttonPressed({ target: button }); + }, + + scrollOnRequest: function(event, data) + { + try { + var line = this.editor.nthLine(data.line); + this.editor.selectLines(line, (data.column-1)); + } catch(e) { + console.log('Exception in scrollOnRequest:', e); + } } }); diff --git a/project/templates/explorer/editor.html b/project/templates/explorer/editor.html index b338e6d3..3c775d4e 100644 --- a/project/templates/explorer/editor.html +++ b/project/templates/explorer/editor.html @@ -123,8 +123,9 @@