Fixes #656: bulk documents upload
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Thu, 12 Aug 2010 10:31:38 +0000 (12:31 +0200)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Thu, 12 Aug 2010 10:31:38 +0000 (12:31 +0200)
apps/wiki/forms.py
apps/wiki/locale/pl/LC_MESSAGES/django.mo
apps/wiki/locale/pl/LC_MESSAGES/django.po
apps/wiki/models.py
apps/wiki/templates/wiki/document_upload.html [new file with mode: 0644]
apps/wiki/urls.py
apps/wiki/views.py
redakcja/static/css/filelist.css

index d8f425a..0a5be8c 100644 (file)
@@ -42,6 +42,26 @@ class DocumentCreateForm(forms.Form):
         return self.cleaned_data
 
 
+class DocumentsUploadForm(forms.Form):
+    """
+        Form used for uploading new documents.
+    """
+    file = forms.FileField(required=True, label=_('ZIP file'))
+
+    def clean(self):
+        file = self.cleaned_data['file']
+
+        import zipfile
+        try:
+            z = self.cleaned_data['zip'] = zipfile.ZipFile(file)
+        except zipfile.BadZipfile:
+            raise forms.ValidationError("Should be a ZIP file.")
+        if z.testzip():
+            raise forms.ValidationError("ZIP file corrupt.")
+
+        return self.cleaned_data
+
+
 class DocumentTextSaveForm(forms.Form):
     """
     Form for saving document's text:
index b16e0a7..0712805 100644 (file)
Binary files a/apps/wiki/locale/pl/LC_MESSAGES/django.mo and b/apps/wiki/locale/pl/LC_MESSAGES/django.mo differ
index 58e8d19..6df7bab 100644 (file)
@@ -3,15 +3,14 @@
 # This file is distributed under the same license as the PACKAGE package.
 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
 #
-#, fuzzy
 msgid ""
 msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
+"Project-Id-Version: Platforma Redakcyjna\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2010-08-03 12:13+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"POT-Creation-Date: 2010-08-12 12:22+0200\n"
+"PO-Revision-Date: 2010-08-12 12:24+0100\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -52,35 +51,39 @@ msgstr "Ostateczna redakcja techniczna"
 msgid "Ready to publish"
 msgstr "Gotowe do publikacji"
 
-#: forms.py:62
+#: forms.py:49
+msgid "ZIP file"
+msgstr "Plik ZIP"
+
+#: forms.py:82
 msgid "Author"
 msgstr "Autor"
 
-#: forms.py:63
+#: forms.py:83
 msgid "Your name"
 msgstr "Imię i nazwisko"
 
-#: forms.py:68
+#: forms.py:88
 msgid "Author's email"
 msgstr "E-mail autora"
 
-#: forms.py:69
+#: forms.py:89
 msgid "Your email address, so we can show a gravatar :)"
 msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
 
-#: forms.py:75
+#: forms.py:95
 msgid "Your comments"
 msgstr "Twój komentarz"
 
-#: forms.py:76
+#: forms.py:96
 msgid "Describe changes you made."
 msgstr "Opisz swoje zmiany"
 
-#: forms.py:82
+#: forms.py:102
 msgid "Completed"
 msgstr "Ukończono"
 
-#: forms.py:83
+#: forms.py:103
 msgid "If you completed a life cycle stage, select it."
 msgstr "Jeśli został ukończony etap prac, wskaż go."
 
@@ -101,7 +104,16 @@ msgstr "motyw"
 msgid "themes"
 msgstr "motywy"
 
-#: views.py:279
+#: views.py:165
+#, python-format
+msgid "Title already used for %s"
+msgstr "Nazwa taka sama jak dla pliku %s"
+
+#: views.py:167
+msgid "Title already used in repository."
+msgstr "Plik o tej nazwie już istnieje w repozytorium."
+
+#: views.py:339
 msgid "Tag added"
 msgstr "Dodano tag"
 
@@ -144,7 +156,7 @@ msgstr "Zapisz"
 
 #: templates/wiki/document_details_base.html:41
 msgid "Save attempt in progress"
-msgstr ""
+msgstr "Trwa zapisywanie"
 
 #: templates/wiki/document_list.html:30
 msgid "Clear filter"
@@ -154,6 +166,46 @@ msgstr "Wyczyść filtr"
 msgid "Your last edited documents"
 msgstr "Twoje ostatnie edycje"
 
+#: templates/wiki/document_upload.html:9
+msgid "Bulk documents upload"
+msgstr "Hurtowe dodawanie dokumentów"
+
+#: templates/wiki/document_upload.html:12
+msgid "Please submit a ZIP with XML files. Files not ending with <code>.xml</code> will be ignored."
+msgstr "Proszę wskazać archiwum ZIP z plikami XML. Pliki nie kończące się na <code>.xml</code> zostaną zignorowane."
+
+#: templates/wiki/document_upload.html:17
+msgid "Upload"
+msgstr "Dodaj"
+
+#: templates/wiki/document_upload.html:24
+msgid "There have been some errors. No files have been added to the repository."
+msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
+
+#: templates/wiki/document_upload.html:25
+msgid "Offending files"
+msgstr "Błędne pliki"
+
+#: templates/wiki/document_upload.html:33
+msgid "Correct files"
+msgstr "Poprawne pliki"
+
+#: templates/wiki/document_upload.html:44
+msgid "Files have been successfully uploaded to the repository."
+msgstr "Pliki zostały dodane do repozytorium."
+
+#: templates/wiki/document_upload.html:45
+msgid "Uploaded files"
+msgstr "Dodane pliki"
+
+#: templates/wiki/document_upload.html:55
+msgid "Skipped files"
+msgstr "Pominięte pliki"
+
+#: templates/wiki/document_upload.html:56
+msgid "Files skipped due to no <code>.xml</code> extension"
+msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
+
 #: templates/wiki/tag_dialog.html:16
 msgid "Cancel"
 msgstr "Anuluj"
@@ -270,3 +322,4 @@ msgstr "Wstaw przypis"
 #: templates/wiki/tabs/wysiwyg_editor_item.html:3
 msgid "Visual editor"
 msgstr "Edytor wizualny"
+
index 3e892ad..438ec5d 100644 (file)
@@ -35,7 +35,7 @@ def normalize_name(name):
     >>> normalize_name("gąska".decode('utf-8'))
     u'g\u0105ska'
     """
-    return name.translate(_PCHARS_DICT).lower()
+    return unicode(name).translate(_PCHARS_DICT).lower()
 
 STAGE_TAGS_RE = re.compile(r'^#stage-finished: (.*)$', re.MULTILINE)
 
diff --git a/apps/wiki/templates/wiki/document_upload.html b/apps/wiki/templates/wiki/document_upload.html
new file mode 100644 (file)
index 0000000..f1e37f7
--- /dev/null
@@ -0,0 +1,69 @@
+{% extends "wiki/base.html" %}
+{% load i18n %}
+{% load wiki %}
+
+
+{% block leftcolumn %}
+
+
+<h2>{% trans "Bulk documents upload" %}</h2>
+
+<p>
+{% trans "Please submit a ZIP with XML files. Files not ending with <code>.xml</code> will be ignored." %}
+</p>
+
+<form enctype="multipart/form-data" method="POST" action="">
+{{ form.as_p }}
+<p><button type="submit">{% trans "Upload" %}</button></p>
+</form>
+
+<hr/>
+
+{% if error_list %}
+
+    <p class='error'>{% trans "There have been some errors. No files have been added to the repository." %}
+    <h3>{% trans "Offending files" %}</h3>
+    <ul id='error-list'>
+        {% for filename, title, error in error_list %}
+            <li>{{title|wiki_title}} (<code>{{ filename }}</code>): {{ error }}</li>
+        {% endfor %}
+    </ul>
+
+    {% if ok_list %}
+    <h3>{% trans "Correct files" %}</h3>
+        <ul>
+            {% for filename, title in ok_list %}
+                <li>{{title|wiki_title}} (<code>{{ filename }}</code>)</li>
+            {% endfor %}
+        </ul>
+    {% endif %}
+
+{% else %}
+
+    {% if ok_list %}
+        <p class='success'>{% trans "Files have been successfully uploaded to the repository." %}</p>
+        <h3>{% trans "Uploaded files" %}</h3>
+        <ul id='ok-list'>
+        {% for filename, title in ok_list %}
+            <li><a href='{% url wiki_editor title %}'>{{ title|wiki_title }}</a> (<code>{{ filename }})</a></li>
+        {% endfor %}
+        </ul>
+    {% endif %}
+{% endif %}
+
+{% if skipped_list %}
+    <h3>{% trans "Skipped files" %}</h3>
+    <p>{% trans "Files skipped due to no <code>.xml</code> extension" %}</p>
+    <ul id='skipped-list'>
+        {% for filename in skipped_list %}
+            <li>{{ filename }}</li>
+        {% endfor %}
+    </ul>
+{% endif %}
+
+
+{% endblock leftcolumn %}
+
+
+{% block rightcolumn %}
+{% endblock rightcolumn %}
index 351ecbc..f4da5f3 100644 (file)
@@ -20,6 +20,9 @@ urlpatterns = patterns('wiki.views',
     url(r'^(?P<name>[^/]+)/readonly$',
         'editor_readonly', name="wiki_editor_readonly"),
 
+    url(r'^upload/$',
+        'upload', name='wiki_upload'),
+
     url(r'^create/(?P<name>[^/]+)',
         'create_missing', name='wiki_create_missing'),
 
index b57347c..7417235 100644 (file)
@@ -13,7 +13,7 @@ from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
 from django import http
 
 from wiki.models import getstorage, DocumentNotFound, normalize_name, split_name, join_name, Theme
-from wiki.forms import DocumentTextSaveForm, DocumentTagForm, DocumentCreateForm
+from wiki.forms import DocumentTextSaveForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm
 from datetime import datetime
 from django.utils.encoding import smart_unicode
 from django.utils.translation import ugettext_lazy as _
@@ -126,7 +126,7 @@ def create_missing(request, name):
         form = DocumentCreateForm(request.POST, request.FILES)
         if form.is_valid():
             doc = storage.create_document(
-                id=form.cleaned_data['id'],
+                name=form.cleaned_data['id'],
                 text=form.cleaned_data['text'],
             )
 
@@ -143,6 +143,58 @@ def create_missing(request, name):
     })
 
 
+def upload(request):
+    storage = getstorage()
+
+    if request.method == "POST":
+        form = DocumentsUploadForm(request.POST, request.FILES)
+        if form.is_valid():
+            zip = form.cleaned_data['zip']
+            skipped_list = []
+            ok_list = []
+            error_list = []
+            titles = {}
+            existing = storage.all()
+            for filename in zip.namelist():
+                if filename[-1] == '/':
+                    continue
+                title = normalize_name(os.path.basename(filename)[:-4])
+                if not (title and filename.endswith('.xml')):
+                    skipped_list.append(filename)
+                elif title in titles:
+                    error_list.append((filename, title, _('Title already used for %s' % titles[title])))
+                elif title in existing:
+                    error_list.append((filename, title, _('Title already used in repository.')))
+                else:
+                    ok_list.append((filename, title))
+                    titles[title] = filename
+            if not error_list:
+                for filename, title in ok_list:
+                    storage.create_document(
+                        name=title,
+                        text=zip.read(filename)
+                    )
+
+            return direct_to_template(request, "wiki/document_upload.html", extra_context={
+                "form": form,
+                "ok_list": ok_list,
+                "skipped_list": skipped_list,
+                "error_list": error_list,
+            })
+                #doc = storage.create_document(
+                #    name=base,
+                #    text=form.cleaned_data['text'],
+
+            
+            return http.HttpResponse('\n'.join(yeslist) + '\n\n' + '\n'.join(nolist))
+    else:
+        form = DocumentsUploadForm()
+
+    return direct_to_template(request, "wiki/document_upload.html", extra_context={
+        "form": form,
+    })
+
+
 @never_cache
 @normalized_name
 def text(request, name):
index b2f51ed..2ccf33e 100644 (file)
@@ -70,3 +70,20 @@ a:hover {
 #loading-overlay {
        display: none;
 }
+
+.error {
+    color: red;
+    font-weight: bold;
+}
+
+.success {
+    color: green;
+}
+
+#error-list {
+    color: red;
+}
+
+#skipped-list {
+    color: #666;
+}
\ No newline at end of file