from django.utils.translation import ugettext_lazy as _
DOCUMENT_TAGS = (
- ("source", _(u"Tekst źródłowy")),
- ("first_correction", _(u"Po autokorekcie")),
- ("tagged", _(u"Tekst otagowany")),
- ("second_correction", _(u"Po korekcie")),
- ("source_annotations", _(u"Sprawdzone przypisy źródła")),
- ("language_updates", _(u"Uwspółcześnienia")),
- ("ready_to_publish", _(u"Tekst do publikacji")),
+ ("source", _("Tekst źródłowy")),
+ ("first_correction", _("Po autokorekcie")),
+ ("tagged", _("Tekst otagowany")),
+ ("second_correction", _("Po korekcie")),
+ ("source_annotations", _("Sprawdzone przypisy źródła")),
+ ("language_updates", _("Uwspółcześnienia")),
+ ("ready_to_publish", _("Tekst do publikacji")),
)
DOCUMENT_TAGS_DICT = dict(DOCUMENT_TAGS)
DOCUMENT_STAGES = (
- ("first_correction", _(u"Autokorekta")),
- ("tagged", _(u"Tagowanie")),
- ("second_correction", _(u"Korekta")),
- ("source_annotations", _(u"Przypisy źródła")),
- ("language_updates", _(u"Uwspółcześnienia")),
+ ("first_correction", _("Autokorekta")),
+ ("tagged", _("Tagowanie")),
+ ("second_correction", _("Korekta")),
+ ("source_annotations", _("Przypisy źródła")),
+ ("language_updates", _("Uwspółcześnienia")),
)
DOCUMENT_STAGES_DICT = dict(DOCUMENT_STAGES)
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from django import forms
-from wiki.models import Document, getstorage
from wiki.constants import DOCUMENT_TAGS, DOCUMENT_STAGES
from django.utils.translation import ugettext_lazy as _
-class DocumentForm(forms.Form):
- """ Old form for saving document's text """
+class DocumentTagForm(forms.Form):
+ """
+ Form for tagging revisions.
+ """
- name = forms.CharField(widget=forms.HiddenInput)
- text = forms.CharField(widget=forms.Textarea)
+ id = forms.CharField(widget=forms.HiddenInput)
+ tag = forms.ChoiceField(choices=DOCUMENT_TAGS)
revision = forms.IntegerField(widget=forms.HiddenInput)
- comment = forms.CharField()
-
- def __init__(self, *args, **kwargs):
- document = kwargs.pop('instance', None)
- super(DocumentForm, self).__init__(*args, **kwargs)
- if document:
- self.fields['name'].initial = document.name
- self.fields['text'].initial = document.text
- self.fields['revision'].initial = document.revision()
-
- def save(self, document_author='anonymous'):
- storage = getstorage()
- document = Document(storage, name=self.cleaned_data['name'], text=self.cleaned_data['text'])
- storage.put(document,
- author=document_author,
- comment=self.cleaned_data['comment'],
- parent=self.cleaned_data['revision'])
+class DocumentCreateForm(forms.Form):
+ """
+ Form used for creating new documents.
+ """
+ title = forms.CharField()
+ id = forms.RegexField(regex=ur"\w+")
+ file = forms.FileField(required=False)
+ text = forms.CharField(required=False, widget=forms.Textarea)
- return storage.get(self.cleaned_data['name'])
+ def clean(self):
+ file = self.cleaned_data['file']
+ if file is not None:
+ try:
+ self.cleaned_data['text'] = file.read().decode('utf-8')
+ except UnicodeDecodeError:
+ raise forms.ValidationError("Text file must be UTF-8 encoded.")
-class DocumentTagForm(forms.Form):
+ if not self.cleaned_data["text"]:
+ raise forms.ValidationError("You must either enter text or upload a file")
- id = forms.CharField(widget=forms.HiddenInput)
- tag = forms.ChoiceField(choices=DOCUMENT_TAGS)
- revision = forms.IntegerField(widget=forms.HiddenInput)
+ return self.cleaned_data
class DocumentTextSaveForm(forms.Form):
- """
+ """if
Form for saving document's text:
* name - document's storage identifier.
# get rid of mimetype
kwargs.pop('mimetype', None)
- super(JSONResponse, self).__init__(
- json.dumps(data, cls=ExtendedEncoder),
- mimetype="application/json", **kwargs)
+ data = json.dumps(data, cls=ExtendedEncoder)
+ print data
+ super(JSONResponse, self).__init__(data, mimetype="application/json", **kwargs)
# return errors
self.vstorage = vstorage.VersionedStorage(path)
def get(self, name, revision=None):
- if revision is None:
- text = self.vstorage.page_text(name)
- else:
- text = self.vstorage.revision_text(name, revision)
- return Document(self, name=name, text=text)
+ text, rev = self.vstorage.page_text(name, revision)
+ return Document(self, name=name, text=text, revision=rev)
+
+ def get_by_tag(self, name, tag):
+ text, rev = self.vstorage.page_text(name, tag)
+ return Document(self, name=name, text=text, revision=rev)
def get_or_404(self, *args, **kwargs):
try:
comment=comment,
parent=parent)
+ return document
+
+ def create_document(self, id, text, title=None):
+ if title is None:
+ title = id.title()
+
+ if text is None:
+ text = u''
+
+ document = Document(self, name=id, text=text, title=title)
+ return self.put(document, u"<wiki>", u"Document created.", None)
+
def delete(self, name, author, comment):
self.vstorage.delete_page(name, author, comment)
for attr, value in kwargs.iteritems():
setattr(self, attr, value)
- def revision(self):
- try:
- return self.storage._info(self.name)[0]
- except DocumentNotFound:
- return - 1
-
def add_tag(self, tag, revision, author):
""" Add document specific tag """
logger.debug("Adding tag %s to doc %s version %d", tag, self.name, revision)
def diff_replace(match):
return """<span class="diff_mark diff_mark_%s">""" % NAMES[match.group(1)]
-
def filter_line(line):
return DIFF_RE.sub(diff_replace, html_escape(line)).replace('\x01', '</span>')
-
def format_changeset(a, b, change):
return (a[0], filter_line(a[1]), b[0], filter_line(b[1]), change)
-
def html_diff_table(la, lb, context=None):
all_changes = difflib._mdiff(la, lb)
--- /dev/null
+{% extends "base.html" %}
+{% load compressed %}
+
+{% block title %}{{ document_name }} - {{ block.super }}{% endblock %}
+
+{% block extrahead %}
+{% compressed_css 'listing' %}
+{% endblock %}
+
+{% block extrabody %}
+{% compressed_js 'listing' %}
+{% endblock %}
+
+{% block maincontent %}
+<h1><img src="{{STATIC_URL}}/img/logo.png">Platforma Redakcyjna</h1>
+<div id="wiki_layout_left_column">
+ {% block leftcolumn %}
+ {% endblock leftcolumn %}
+</div>
+<div id="wiki_layout_right_column">
+ {% block rightcolumn %}
+ {% endblock rightcolumn %}
+</div>
+{% endblock maincontent %}
\ No newline at end of file
--- /dev/null
+{% extends "wiki/base.html" %}
+
+{% block leftcolumn %}
+ <form enctype="multipart/form-data" method="POST" action="">
+ {{ form.as_p }}
+
+ <p><button type="submit">Stwórz utwór</button></p>
+ </form>
+{% endblock leftcolumn %}
+
+{% block rightcolumn %}
+
+{% endblock rightcolumn %}
\ No newline at end of file
-{% extends "base.html" %}
-{% load compressed %}
+{% extends "wiki/base.html" %}
-{% block extrahead %}
-{% compressed_css 'listing' %}
-{% endblock extrahead %}
{% block extrabody %}
-{% compressed_js 'listing' %}
+{{ block.super }}
<script type="text/javascript" charset="utf-8">
$(function() {
function search(event) {
</script>
{% endblock %}
-{% block maincontent %}
-<h1><img src="{{STATIC_URL}}/img/logo.png">Platforma Redakcyjna</h1>
-
-
-<div id="document-list">
+{% block leftcolumn %}
<form method="get" action="#">
<table id="file-list">
<thead>
</tbody>
</table>
</form>
+{% endblock leftcolumn %}
+{% block rightcolumn %}
<div id="last-edited-list">
<h2>Twoje ostatnio otwierane dokumenty:</h2>
<ol>
{% endfor %}
</ol>
</div>
-</div>
-
-</div>
-{% endblock maincontent %}
+{% endblock rightcolumn %}
<div id="history-view-editor" class="editor" style="display: none">
<div class="toolbar">
- <button type="button" id="make-diff-button">Porównaj</button>
+ <button type="button" id="make-diff-button">Porównaj</button>
<button type="button" id="tag-changeset-button">Oznacz wersje</button>
- </div>
+ </div>
<div id="history-view">
<p class="message-box" style="display:none;"></p>
-
+
<table id="changes-list-container">
- <tbody id="changes-list">
+ <tbody id="changes-list">
</tbody>
<tbody style="display: none;">
- <tr class="entry row-stub">
+ <tr class="entry row-stub">
<td data-stub-value="version"></td>
<td>
<span data-stub-value="description"></span>
<br />
<span data-stub-value="author"></span>, <span data-stub-value="date"></span>
</td>
- <td data-stub-value="tag">
+ <td data-stub-value="tag">
</td>
</tr>
</tbody>
- </table>
+ </table>
</div>
</div>
<div id="save_dialog" class="dialog" data-ui-jsclass="SaveDialog">
- <form method="POST" action="#">
+ <form method="POST" action="">
<p>{{ forms.text_save.comment.label }}</p>
<p class="help_text">
{{ forms.text_save.comment.help_text}}
<span data-ui-error-for="{{ forms.text_save.comment.name }}"> </span>
</p>
{{forms.text_save.comment }}
-
-
-
+
+
+
{% if request.user.is_anonymous %}
<p>
- {{ forms.text_save.author.label }}:
+ {{ forms.text_save.author.label }}:
{{ forms.text_save.author }}
<span class="help_text">{{ forms.text_save.author.help_text }}</span>
<span data-ui-error-for="{{ forms.text_save.author.name }}"> </span>
</p>
{% else %}
<p>
- {{ forms.text_save.stage_completed.label }}:
+ {{ forms.text_save.stage_completed.label }}:
{{ forms.text_save.stage_completed }}
<span class="help_text">{{ forms.text_save.stage_completed.help_text }}</span>
<span data-ui-error-for="{{ forms.text_save.stage_completed.name }}"> </span>
</p>
{% endif %}
-
-
+
+
{% for f in forms.text_save.hidden_fields %}
{{ f }}
{% endfor %}
-
+
<p data-ui-error-for="__all__"> </p>
-
+
<p class="action_area">
<button type="submit" class"ok" data-ui-action="save">Zapisz</button>
<button type="button" class="cancel" data-ui-action="cancel">Anuluj</button>
- </p>
- </form>
+ </p>
+ </form>
</div>
</div> -->
<div id="summary-view">
<img class="book-cover" src="{{MEDIA_URL}}images/empty.png">
- <form>
+
<h2>
<label for="title">Tytuł:</label>
<span data-ui-editable="true" data-edit-target="meta.displayTitle"
<span data-ui-editable="true" data-edit-target="meta.galleryLink"
>{{ document_meta.gallery}}</span>
</p>
- </form>
-
+
+ <p><button type="button">Publikuj na wolnelektury.pl</button></p>
</div>
</div>
\ No newline at end of file
<div id="add_tag_dialog" class="dialog" data-ui-jsclass="AddTagDialog">
<form method="POST" action="#">
{% for field in forms.add_tag.visible_fields %}
- <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
- <p>{{ field.help_text }}</p>
+ <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
+ <p>{{ field.help_text }}</p>
{% endfor %}
-
-
- {% for f in forms.add_tag.hidden_fields %}
+
+
+ {% for f in forms.text_save.hidden_fields %}
{{ f }}
{% endfor %}
- <p data-ui-error-for="__all__"> </p>
-
+ <p data-ui-error-for="__all__"> </p>
+
<p class="action_area">
<button type="submit" class"ok" data-ui-action="save">Zapisz</button>
<button type="button" class="cancel" data-ui-action="cancel">Anuluj</button>
- </p>
+ </p>
</form>
</div>
urlpatterns = patterns('wiki.views',
url(r'^$',
'document_list', name='wiki_doclist'),
+ url(r'^create/(?P<name>[^/]+)',
+ 'document_create_missing', name='wiki_create_missing'),
url(r'^gallery/(?P<directory>[^/]+)$',
'document_gallery', name="wiki_gallery"),
url(r'^(?P<name>[^/]+)/history$',
'document_diff', name="wiki_diff"),
url(r'^(?P<name>[^/]+)/tags$',
'document_add_tag', name="wiki_add_tag"),
+ url(r'^(?P<name>[^/]+)/tags$',
+ 'document_publish'),
url(r'^(?P<name>[^/]+)$',
'document_detail', name="wiki_details"),
)
from django.views.generic.simple import direct_to_template
from django.views.decorators.http import require_POST
+from django.core.urlresolvers import reverse
from wiki.helpers import JSONResponse, JSONFormInvalid, JSONServerError, ajax_require_permission
from django import http
-from wiki.models import getstorage
-from wiki.forms import DocumentTextSaveForm, DocumentTagForm
+from wiki.models import getstorage, DocumentNotFound
+from wiki.forms import DocumentTextSaveForm, DocumentTagForm, DocumentCreateForm
from datetime import datetime
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
@never_cache
def document_detail(request, name, template_name='wiki/document_details.html'):
- document = getstorage().get_or_404(name)
+ try:
+ document = getstorage().get(name)
+ except DocumentNotFound:
+ return http.HttpResponseRedirect(reverse("wiki_create_missing", args=[name]))
access_time = datetime.now()
last_documents = request.session.get("wiki_last_docs", {})
return direct_to_template(request, template_name, extra_context={
'document': document,
+ 'document_name': document.name,
'document_info': document.info,
'document_meta': document.meta,
- 'forms': {"text_save": DocumentTextSaveForm(), "add_tag": DocumentTagForm()},
+ 'forms': {
+ "text_save": DocumentTextSaveForm(prefix="textsave"),
+ "add_tag": DocumentTagForm(prefix="addtag")
+ },
+ })
+
+
+def document_create_missing(request, name):
+ storage = getstorage()
+
+ if request.method == "POST":
+ form = DocumentCreateForm(request.POST, request.FILES)
+ if form.is_valid():
+ doc = storage.create_document(
+ id=form.cleaned_data['id'],
+ text=form.cleaned_data['text'],
+ )
+
+ return http.HttpResponseRedirect(reverse("wiki_details", args=[doc.name]))
+ else:
+ form = DocumentCreateForm(initial={
+ "id": name.replace(" ", "_"),
+ "title": name.title(),
+ })
+
+ return direct_to_template(request, "wiki/document_create_missing.html", extra_context={
+ "document_name": name,
+ "form": form,
})
@never_cache
def document_text(request, name):
storage = getstorage()
- document = storage.get_or_404(name)
if request.method == 'POST':
- form = DocumentTextSaveForm(request.POST)
+ form = DocumentTextSaveForm(request.POST, prefix="textsave")
if form.is_valid():
revision = form.cleaned_data['parent_revision']
+
+ document = storage.get_or_404(name, revision)
document.text = form.cleaned_data['text']
storage.put(document,
parent=revision,
)
+ document = storage.get(name)
+
return JSONResponse({
- 'text': document.plain_text if revision != document.revision() else None,
+ 'text': document.plain_text if revision != document.revision else None,
'meta': document.meta(),
- 'revision': document.revision(),
+ 'revision': document.revision,
})
else:
return JSONFormInvalid(form)
else:
+ revision = request.GET.get("revision", None)
+
+ try:
+ try:
+ revision = revision and int(revision)
+ logger.info("Fetching %s", revision)
+ document = storage.get(name, revision)
+ except ValueError:
+ # treat as a tag
+ logger.info("Fetching tag %s", revision)
+ document = storage.get_by_tag(name, revision)
+ except DocumentNotFound:
+ raise http.Http404
+
return JSONResponse({
'text': document.plain_text,
'meta': document.meta(),
- 'revision': document.revision(),
+ 'revision': document.revision,
})
images = [map_to_url(f) for f in map(smart_unicode, os.listdir(base_dir)) if is_image(f)]
images.sort()
return JSONResponse(images)
- except (IndexError, OSError), exc:
- import traceback
- traceback.print_exc()
+ except (IndexError, OSError) as e:
+ logger.exception("Unable to fetch gallery")
raise http.Http404
storage = getstorage()
# TODO: pagination
- changesets = storage.history(name)
+ changesets = list(storage.history(name))
return JSONResponse(changesets)
def document_add_tag(request, name):
storage = getstorage()
- form = DocumentTagForm(request.POST)
+ form = DocumentTagForm(request.POST, prefix="addtag")
if form.is_valid():
doc = storage.get_or_404(form.cleaned_data['id'])
doc.add_tag(tag=form.cleaned_data['tag'],
@require_POST
@ajax_require_permission('wiki.can_publish')
-def document_publish(request, name, version):
+def document_publish(request, name):
storage = getstorage()
+ document = storage.get_by_tag(name, "ready_to_publish")
- # get the document
- document = storage.get_or_404(name, revision=int(version))
+ api = wlapi.WLAPI(**settings.WL_API_CONFIG)
- api = wlapi.WLAPI(settings.WL_API_CONFIG)
try:
return JSONResponse({"result": api.publish_book(document)})
except wlapi.APICallException, e:
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+
+from nose.tools import *
+from nose.core import runmodule
+
+import wlapi
+
+
+class FakeDocument():
+
+ def __init__(self):
+ self.text = "Some Text"
+
+
+class TestWLAPI(object):
+
+ def setUp(self):
+ self.api = wlapi.WLAPI(
+ URL="http://localhost:7000/api/",
+ AUTH_REALM="WL API",
+ AUTH_USER="platforma",
+ AUTH_PASSWD="platforma",
+ )
+
+ def test_basic_call(self):
+ assert_equal(self.api.list_books(), [])
+
+ def test_publish_book(self):
+ self.api.publish_book(FakeDocument())
+
+if __name__ == '__main__':
+ runmodule()
@with_working_copy_locked
@with_storage_locked
- def save_file(self, title, file_name, author=u'', comment=u'', parent=None):
+ def save_file(self, title, file_name, **kwargs):
"""Save an existing file as specified page."""
- user = author.encode('utf-8') or u'anonymous'.encode('utf-8')
- text = comment.encode('utf-8') or u'comment'.encode('utf-8')
+
+ author = kwargs.get('author', u'anonymous').encode('utf-8')
+ comment = kwargs.get('comment', u'Empty comment.').encode('utf-8')
+ parent = kwargs.get('parent', None)
repo_file = self._title_to_file(title)
file_path = self._file_path(title)
current_page_rev = -1
if parent is not None and current_page_rev != parent:
- msg = self.merge_changes(changectx, repo_file, text, user, parent)
- user = '<wiki>'
- text = msg.encode('utf-8')
+ msg = self.merge_changes(changectx, repo_file, comment, author, parent)
+ author = '<wiki>'
+ comment = msg.encode('utf-8')
- self._commit([repo_file], text, user)
+ self._commit([repo_file], comment, author)
def save_data(self, title, data, **kwargs):
"""Save data as specified page."""
f = open(file_path, "wb")
f.write(data)
f.close()
- self.save_file(title=title, file_name=file_path, **kwargs)
+
+ return self.save_file(title=title, file_name=file_path, **kwargs)
finally:
try:
os.unlink(file_path)
except OSError:
pass
- def save_text(self, text, **kwargs):
+ def save_text(self, **kwargs):
"""Save text as specified page, encoded to charset."""
- self.save_data(data=text.encode(self.charset), **kwargs)
+ text = kwargs.pop('text')
+ return self.save_data(data=text.encode(self.charset), **kwargs)
- def _commit(self, files, text, user):
+ def _commit(self, files, comment, user):
match = mercurial.match.exact(self.repo_path, '', list(files))
- return self.repo.commit(match=match, text=text, user=user, force=True)
-
- def page_text(self, title):
- """Read unicode text of a page."""
- data = self.open_page(title).read()
- text = unicode(data, self.charset, 'replace')
- return text
-
- def page_lines(self, page):
- for data in page:
- yield unicode(data, self.charset, 'replace')
+ return self.repo.commit(match=match, text=comment, user=user, force=True)
@with_working_copy_locked
@with_storage_locked
self.repo.remove([repo_file])
self._commit([repo_file], text, user)
- @with_working_copy_locked
- def open_page(self, title):
- if title not in self:
- raise DocumentNotFound()
+# @with_working_copy_locked
+# def _open_page(self, title):
+# if title not in self:
+# raise DocumentNotFound()
+#
+# path = self._title_to_file(title)
+# logger.debug("Opening page %s", path)
+# try:
+# return self.repo.wfile(path, 'rb')
+# except IOError:
+# logger.exception("Failed to open page %s", title)
+# raise DocumentNotFound()
+
+ def page_text(self, title, revision=None):
+ """Read unicode text of a page."""
+ ctx = self._find_filectx(title, revision)
+ return ctx.data().decode(self.charset, 'replace'), ctx.filerev()
+
+ def page_text_by_tag(self, title, tag):
+ """Read unicode text of a taged page."""
+ tag = u"{title}#{tag}".format(**locals()).encode('utf-8')
+ fname = self._title_to_file(title)
- path = self._title_to_file(title)
- logger.debug("Opening page %s", path)
try:
- return self.repo.wfile(path, 'rb')
- except IOError:
- logger.exception("Failed to open page %s", title)
+ ctx = self.repo[tag][fname]
+ return ctx.data().decode(self.charset, 'replace'), ctx.filerev()
+ except IndexError:
raise DocumentNotFound()
@with_working_copy_locked
def page_file_meta(self, title):
"""Get page's inode number, size and last modification time."""
try:
- (st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size,
- st_atime, st_mtime, st_ctime) = os.stat(self._file_path(title))
+ (_st_mode, st_ino, _st_dev, _st_nlink, _st_uid, _st_gid, st_size,
+ _st_atime, st_mtime, _st_ctime) = os.stat(self._file_path(title))
except OSError:
return 0, 0, 0
return st_ino, st_size, st_mtime
rev = filectx_tip.filerev()
filectx = filectx_tip.filectx(rev)
date = datetime.datetime.fromtimestamp(filectx.date()[0])
- author = unicode(filectx.user(), "utf-8",
- 'replace').split('<')[0].strip()
- comment = unicode(filectx.description(), "utf-8", 'replace')
+ author = filectx.user().decode("utf-8", 'replace')
+ comment = filectx.description().decode("utf-8", 'replace')
return rev, date, author, comment
def repo_revision(self):
"""Find the last revision in which the file existed."""
repo_file = self._title_to_file(title)
- changectx = self._changectx()
+
+ changectx = self._changectx() # start with tip
stack = [changectx]
+
while repo_file not in changectx:
if not stack:
return None
+
changectx = stack.pop()
for parent in changectx.parents():
if parent != changectx:
try:
fctx = changectx[repo_file]
- return fctx if rev is None else fctx.filectx(rev)
- except IndexError, LookupError:
+
+ if rev is not None:
+ fctx = fctx.filectx(rev)
+ fctx.filerev()
+
+ return fctx
+ except (IndexError, LookupError) as e:
raise DocumentNotFound()
def page_history(self, title):
for rev in range(maxrev, minrev - 1, -1):
filectx = filectx_tip.filectx(rev)
date = datetime.datetime.fromtimestamp(filectx.date()[0])
- author = unicode(filectx.user(), "utf-8",
- 'replace').split('<')[0].strip()
- comment = unicode(filectx.description(), "utf-8", 'replace')
+ author = filectx.user().decode('utf-8', 'replace')
+ comment = filectx.description().decode("utf-8", 'replace')
tags = [t.rsplit('#', 1)[-1] for t in filectx.changectx().tags() if '#' in t]
yield {
"tag": tags,
}
- def page_revision(self, title, rev):
- """Get unicode contents of specified revision of the page."""
- return self._find_filectx(title, rev).data()
-
- def revision_text(self, title, rev):
- data = self.page_revision(title, rev)
- text = unicode(data, self.charset, 'replace')
- return text
-
@with_working_copy_locked
def add_page_tag(self, title, rev, tag, user, doctag=True):
if doctag:
- tag = "{title}#{tag}".format(**locals())
+ tag = u"{title}#{tag}".format(**locals()).encode('utf-8')
- message = "Assigned tag {tag} to version {rev} of {title}".format(**locals())
+ message = u"Assigned tag {tag!r} to version {rev!r} of {title!r}".format(**locals()).encode('utf-8')
fctx = self._find_filectx(title, rev)
self.repo.tag(
for wiki_rev in range(maxrev, minrev - 1, -1):
change = self.repo.changectx(wiki_rev)
date = datetime.datetime.fromtimestamp(change.date()[0])
- author = unicode(change.user(), "utf-8",
- 'replace').split('<')[0].strip()
- comment = unicode(change.description(), "utf-8", 'replace')
+ author = change.user().decode('utf-8', 'replace')
+ comment = change.description().decode("utf-8", 'replace')
for repo_file in change.files():
if repo_file.startswith(self.repo_prefix):
title = self._file_to_title(repo_file)
class APICallException(Exception):
- pass
+
+ def __init__(self, cause=None):
+ super(Exception, self).__init__()
+ self.cause = cause
+
+ def __unicode__(self):
+ return u"%s, cause: %s" % (type(self).__name__, repr(self.cause))
+
+ def __str__(self):
+ return self.__unicode__().encode('utf-8')
def api_call(path, format="json"):
# prepare request
rq = urllib2.Request(self.base_url + path + ".json")
- # will send POST when there is data, GET otherwise
+ # will send POST when there is data, GET otherwise
if data is not None:
rq.add_data(json.dumps(data))
rq.add_header("Content-Type", "application/json")
class WLAPI(object):
- def __init__(self, config_dict):
+ def __init__(self, **config_dict):
self.base_url = config_dict['URL']
self.auth_realm = config_dict['AUTH_REALM']
self.auth_user = config_dict['AUTH_USER']
- auth_handler = urllib2.HTTPDigestAuthHandler()
- auth_handler.add_password(
+ digest_handler = urllib2.HTTPDigestAuthHandler()
+ digest_handler.add_password(
+ realm=self.auth_realm, uri=self.base_url,
+ user=self.auth_user, passwd=config_dict['AUTH_PASSWD'])
+
+ basic_handler = urllib2.HTTPBasicAuthHandler()
+ basic_handler.add_password(
realm=self.auth_realm, uri=self.base_url,
user=self.auth_user, passwd=config_dict['AUTH_PASSWD'])
- self.opener = urllib2.build_opener(auth_handler)
+ self.opener = urllib2.build_opener(digest_handler, basic_handler)
def _http_error(self, error):
- return self._error()
+ message = error.read()
+ logger.debug("HTTP ERROR: %s", message)
+ return self._error(message)
def _error(self, error):
- raise APICallException(cause=error)
+ raise APICallException(error)
+
+ @api_call("books")
+ def list_books(self):
+ yield
@api_call("books")
def publish_book(self, document):
--- /dev/null
+# CSS and JS files to compress
+COMPRESS_CSS = {
+ 'detail': {
+ 'source_filenames': (
+ 'css/master.css',
+ 'css/gallery.css',
+ 'css/history.css',
+ 'css/summary.css',
+ 'css/html.css',
+ 'css/jquery.autocomplete.css',
+ 'css/dialogs.css',
+ ),
+ 'output_filename': 'compressed/detail_styles_?.css',
+ },
+ 'listing': {
+ 'source_filenames': (
+ 'css/filelist.css',
+ ),
+ 'output_filename': 'compressed/listing_styles_?.css',
+ }
+}
+
+COMPRESS_JS = {
+ # everything except codemirror
+ 'detail': {
+ 'source_filenames': (
+ # libraries
+ 'js/jquery-1.4.2.min.js',
+ 'js/jquery.autocomplete.js',
+ 'js/jquery.blockui.js',
+ 'js/jquery.elastic.js',
+ 'js/button_scripts.js',
+ 'js/slugify.js',
+
+ # wiki scripts
+ 'js/wiki/wikiapi.js',
+ 'js/wiki/xslt.js',
+
+ # base UI
+ 'js/wiki/base.js',
+
+ # dialogs
+ 'js/wiki/dialog_save.js',
+
+ # views
+ 'js/wiki/view_history.js',
+ 'js/wiki/view_summary.js',
+ 'js/wiki/view_editor_source.js',
+ 'js/wiki/view_editor_wysiwyg.js',
+ 'js/wiki/view_gallery.js',
+ 'js/wiki/view_column_diff.js',
+
+ # bootstrap
+ 'js/wiki/loader.js',
+ ),
+ 'output_filename': 'compressed/detail_scripts_?.js',
+ },
+ 'listing': {
+ 'source_filenames': (
+ 'js/jquery-1.4.2.min.js',
+ 'js/slugify.js',
+ ),
+ 'output_filename': 'compressed/listing_scripts_?.js',
+ }
+}
+
+COMPRESS = True
+COMPRESS_CSS_FILTERS = None
+COMPRESS_JS_FILTERS = None
+COMPRESS_AUTO = False
+COMPRESS_VERSION = True
+COMPRESS_VERSIONING = 'compress.versioning.hash.MD5Versioning'
def settings(request):
from django.conf import settings
return {'MEDIA_URL': settings.MEDIA_URL,
- 'STATIC_URL': settings.STATIC_URL}
+ 'STATIC_URL': settings.STATIC_URL,
+ 'REDMINE_URL': settings.REDMINE_URL}
# REPOSITORY_PATH = '/Users/zuber/Projekty/platforma/files/books'
IMAGE_DIR = 'images'
+
+WL_API_CONFIG = {
+ "URL": "http://localhost:7000/api/",
+ "AUTH_REALM": "wlapi",
+ "AUTH_USER": "platforma",
+ "AUTH_PASSWD": "platforma",
+}
+
# Import localsettings file, which may override settings defined here
try:
from localsettings import *
#summary-view .book-cover {
float: left;
margin: 1em;
+ min-height: 300px;
+ min-width: 240px;
+
max-height: 300px;
+ max-width: 240px;
}
-#summary-view form p {
+#summary-view p {
margin: 0.5em;
}
-#summary-view form label {
+#summary-view label {
font-weight: bold;
}
var $stub = $('#history-view .row-stub');
changes_list.html('');
- var tags = $('select#id_tag option');
+ var tags = $('select#id_addtag_tag option');
$.each(data, function(){
$.wiki.renderStub({
function SummaryPerspective(options) {
var old_callback = options.callback;
+ var self = this;
+
options.callback = function(){
+ $('#publish_button').click(function() {
+ $.blockUI({message: "Oczekiwanie na odpowiedź serwera..."});
+ self.doc.publish({
+ success: function(doc, data) {
+ $.blockUI({message: "Udało się", timeout: 2000});
+ },
+ failure: function(doc, message) {
+ $.blockUI({
+ message: message,
+ timeout: 5000
+ });
+ }
+
+ });
+ });
+
old_callback.call(this);
};
(function($) {
-
$.wikiapi = {};
- var noop = function() {};
- var noops = {success: noop, failure: noop};
-
+ var noop = function() {
+ };
+ var noops = {
+ success: noop,
+ failure: noop
+ };
/*
* Return absolute reverse path of given named view.
* (at least he have it hard-coded in one place)
* TODO: think of a way, not to hard-code it here ;)
*
*/
- function reverse() {
+ function reverse() {
var vname = arguments[0];
-
- if(vname == "ajax_document_text") {
+ var base_path = "/documents";
+
+ if (vname == "ajax_document_text") {
var path = "/" + arguments[1] + "/text";
- if (arguments[2] !== undefined)
+
+ if (arguments[2] !== undefined)
path += "/" + arguments[2];
- return path;
+
+ return base_path + path;
}
-
+
if (vname == "ajax_document_history") {
- return "/" + arguments[1] + "/history";
+
+ return base_path + "/" + arguments[1] + "/history";
}
-
+
if (vname == "ajax_document_gallery") {
- return "/gallery/" + arguments[1];
+
+ return base_path + "/gallery/" + arguments[1];
}
-
- if(vname == "ajax_document_diff")
- return "/" + arguments[1] + "/diff";
-
- if(vname == "ajax_document_addtag")
- return "/" + arguments[1] + "/tags";
-
- console.log("Couldn't reverse match:", vname);
+
+ if (vname == "ajax_document_diff")
+ return base_path + "/" + arguments[1] + "/diff";
+
+ if (vname == "ajax_document_addtag")
+ return base_path + "/" + arguments[1] + "/tags";
+
+ if (vname == "ajax_publish")
+ return base_path + "/" + arguments[1] + "/publish";
+
+ console.log("Couldn't reverse match:", vname);
return "/404.html";
};
-
+
/*
* Document Abstraction
*/
function WikiDocument(element_id) {
- var meta = $('#'+element_id);
-
+ var meta = $('#' + element_id);
this.id = meta.attr('data-document-name');
this.revision = $("*[data-key='revision']", meta).text();
this.galleryLink = $("*[data-key='gallery']", meta).text();
this.galleryImages = [];
this.text = null;
-
this.has_local_changes = false;
this._lock = -1;
this._context_lock = -1;
this._lock_count = 0;
};
-
-
+
WikiDocument.prototype.triggerDocumentChanged = function() {
$(document).trigger('wlapi_document_changed', this);
};
-
/*
* Fetch text of this document.
*/
WikiDocument.prototype.fetch = function(params) {
params = $.extend({}, noops, params);
var self = this;
-
$.ajax({
method: "GET",
url: reverse("ajax_document_text", self.id),
dataType: 'json',
- success: function(data)
- {
+ success: function(data) {
var changed = false;
-
- if (self.text === null || self.revision !== data.revision) {
+
+if (self.text === null || self.revision !== data.revision) {
self.text = data.text;
self.revision = data.revision;
self.gallery = data.gallery;
changed = true;
self.triggerDocumentChanged();
};
-
+
self.has_local_changes = false;
params['success'](self, changed);
},
}
});
};
-
/*
* Fetch history of this document.
*
/* this doesn't modify anything, so no locks */
params = $.extend({}, noops, params);
var self = this;
-
$.ajax({
method: "GET",
url: reverse("ajax_document_history", self.id),
dataType: 'json',
- data: {"from": params['from'], "upto": params['upto']},
+ data: {
+ "from": params['from'],
+ "upto": params['upto']
+ },
success: function(data) {
params['success'](self, data);
},
}
});
};
-
WikiDocument.prototype.fetchDiff = function(params) {
/* this doesn't modify anything, so no locks */
var self = this;
-
params = $.extend({
'from': self.revision,
'to': self.revision
}, noops, params);
-
$.ajax({
method: "GET",
url: reverse("ajax_document_diff", self.id),
dataType: 'html',
- data: {"from": params['from'], "to": params['to']},
+ data: {
+ "from": params['from'],
+ "to": params['to']
+ },
success: function(data) {
params['success'](self, data);
},
}
});
};
-
/*
* Fetch gallery
*/
WikiDocument.prototype.refreshGallery = function(params) {
params = $.extend({}, noops, params);
var self = this;
-
$.ajax({
method: "GET",
url: reverse("ajax_document_gallery", self.galleryLink),
}
});
};
-
/*
* Set document's text
*/
this.text = text;
this.has_local_changes = true;
};
-
/*
* Set document's gallery link
*/
this.galleryLink = gallery;
this.has_local_changes = true;
};
-
/*
* Save text back to the server
*/
- WikiDocument.prototype.save = function(params){
+ WikiDocument.prototype.save = function(params) {
params = $.extend({}, noops, params);
var self = this;
-
+
if (!self.has_local_changes) {
- console.log("Abort: no changes.");
+ console.log("Abort: no changes.");
return params['success'](self, false, "Nie ma zmian do zapisania.");
};
-
+
// Serialize form to dictionary
var data = {};
$.each(params['form'].serializeArray(), function() {
data[this.name] = this.value;
});
-
var metaComment = '<!--';
metaComment += '\n\tgallery:' + self.galleryLink;
metaComment += '\n-->\n'
-
- data.text = metaComment + self.text;
- data.comment = data.comment;
-
+
+ data['textsave-text'] = metaComment + self.text;
+
$.ajax({
url: reverse("ajax_document_text", self.id),
type: "POST",
dataType: "json",
data: data,
- success: function(data){
+ success: function(data) {
var changed = false;
+
if (data.text) {
self.text = data.text;
self.revision = data.revision;
self.gallery = data.gallery;
changed = true;
self.triggerDocumentChanged();
- }
- params['success'](self, changed,
- ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna") );
+ };
+
+ params['success'](self, changed, ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna"));
},
error: function(xhr) {
try {
params['failure'](self, $.parseJSON(xhr.responseText));
- }
- catch(e) {
- params['failure'](self, {"__message": "<p>Nie udało się zapisać - błąd serwera.</p>"});
+ }
+ catch (e) {
+ params['failure'](self, {
+ "__message": "<p>Nie udało się zapisać - błąd serwera.</p>"
+ });
};
+
}
});
}; /* end of save() */
-
+ WikiDocument.prototype.publish = function(params) {
+ params = $.extend({}, noops, params);
+ var self = this;
+ $.ajax({
+ url: reverse("ajax_publish", self.id),
+ type: "POST",
+ dataType: "json",
+ success: function(data) {
+ params.success(self, data);
+ },
+ error: function(xhr) {
+ if (xhr.status == 403 || xhr.status == 401) {
+ params.failure(self, "Nie masz uprawnień lub nie jesteś zalogowany.");
+ }
+ else {
+ try {
+ params.failure(self, xhr.responseText);
+ }
+ catch (e) {
+ params.failure(self, "Nie udało się - błąd serwera.");
+ };
+ };
+
+ }
+ });
+ };
WikiDocument.prototype.setTag = function(params) {
params = $.extend({}, noops, params);
var self = this;
-
var data = {
"id": self.id,
};
-
/* unpack form */
$.each(params.form.serializeArray(), function() {
data[this.name] = this.value;
});
-
$.ajax({
url: reverse("ajax_document_addtag", self.id),
type: "POST",
dataType: "json",
data: data,
- success: function(data){
+ success: function(data) {
params.success(self, data.message);
},
- error: function(xhr) {
+ error: function(xhr) {
if (xhr.status == 403 || xhr.status == 401) {
params.failure(self, {
"__all__": ["Nie masz uprawnień lub nie jesteś zalogowany."]
else {
try {
params.failure(self, $.parseJSON(xhr.responseText));
- }
+ }
catch (e) {
params.failure(self, {
"__all__": ["Nie udało się - błąd serwera."]
});
};
+
};
+
}
});
};
-
$.wikiapi.WikiDocument = WikiDocument;
-
})(jQuery);
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
url(r'^%s(?P<path>.+)$' % settings.STATIC_URL[1:], 'django.views.static.serve',
{'document_root': settings.STATIC_ROOT, 'show_indexes': True}),
- url(r'^', include('wiki.urls')),
+
+ url(r'^$', 'redirect_to', {'url': '/documents/'}),
+ url(r'^documents/', include('wiki.urls')),
)